448 changes: 448 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//===--- UseStdNumbersCheck.h - clang-tidy ----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDNUMBERSCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDNUMBERSCHECK_H

#include "../ClangTidyCheck.h"
#include "../utils/IncludeInserter.h"

namespace clang::tidy::modernize {

/// Finds constants and function calls to math functions that can be replaced
/// with c++20's mathematical constants from the ``numbers`` header and
/// offers fix-it hints.
/// Does not match the use of variables with that value, and instead,
/// offers a replacement at the definition of those variables.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-numbers.html
class UseStdNumbersCheck : public ClangTidyCheck {
public:
UseStdNumbersCheck(StringRef Name, ClangTidyContext *Context);

bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus20;
}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}

private:
utils::IncludeInserter IncludeInserter;
StringRef DiffThresholdString;
double DiffThreshold;
};

} // namespace clang::tidy::modernize

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDNUMBERSCHECK_H
7 changes: 7 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ New checks
replacing with ``starts_with`` when the method exists in the class. Notably,
this will work with ``std::string`` and ``std::string_view``.

- New :doc:`modernize-use-std-numbers
<clang-tidy/checks/modernize/use-std-numbers>` check.

Finds constants and function calls to math functions that can be replaced
with C++20's mathematical constants from the ``numbers`` header and
offers fix-it hints.

- New :doc:`performance-enum-size
<clang-tidy/checks/performance/enum-size>` check.

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ Clang-Tidy Checks
:doc:`modernize-use-nullptr <modernize/use-nullptr>`, "Yes"
:doc:`modernize-use-override <modernize/use-override>`, "Yes"
:doc:`modernize-use-starts-ends-with <modernize/use-starts-ends-with>`, "Yes"
:doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
:doc:`modernize-use-std-print <modernize/use-std-print>`, "Yes"
:doc:`modernize-use-trailing-return-type <modernize/use-trailing-return-type>`, "Yes"
:doc:`modernize-use-transparent-functors <modernize/use-transparent-functors>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
.. title:: clang-tidy - modernize-use-std-numbers

modernize-use-std-numbers
=========================

Finds constants and function calls to math functions that can be replaced
with C++20's mathematical constants from the ``numbers`` header and offers
fix-it hints.
Does not match the use of variables with that value, and instead,
offers a replacement for the definition of those variables.
Function calls that match the pattern of how the constant is calculated are
matched and replaced with the ``std::numbers`` constant.
The use of macros gets replaced with the corresponding ``std::numbers``
constant, instead of changing the macro definition.

The following list of constants from the ``numbers`` header are supported:

* ``e``
* ``log2e``
* ``log10e``
* ``pi``
* ``inv_pi``
* ``inv_sqrtpi``
* ``ln2``
* ``ln10``
* ``sqrt2``
* ``sqrt3``
* ``inv_sqrt3``
* ``egamma``
* ``phi``

The list currently includes all constants as of C++20.

The replacements use the type of the matched constant and can remove explicit
casts, i.e., switching between ``std::numbers::e``,
``std::numbers::e_v<float>`` and ``std::numbers::e_v<long double>`` where
appropriate.

.. code-block:: c++

double sqrt(double);
double log2(double);
void sink(auto&&) {}
void floatSink(float);

#define MY_PI 3.1415926

void foo() {
const double Pi = 3.141592653589; // const double Pi = std::numbers::pi
const auto Use = Pi / 2; // no match for Pi
static constexpr double Euler = 2.7182818; // static constexpr double Euler = std::numbers::e;

log2(exp(1)); // std::numbers::log2e;
log2(Euler); // std::numbers::log2e;
1 / sqrt(MY_PI); // std::numbers::inv_sqrtpi;
sink(MY_PI); // sink(std::numbers::pi);
floatSink(MY_PI); // floatSink(std::numbers::pi);
floatSink(static_cast<float>(MY_PI)); // floatSink(std::numbers::pi_v<float>);
}

Options
-------

.. option:: DiffThreshold

A floating point value that sets the detection threshold for when literals
match a constant. A literal matches a constant if
``abs(literal - constant) < DiffThreshold`` evaluates to ``true``. Default
is `0.001`.

.. option:: IncludeStyle

A string specifying which include-style is used, `llvm` or `google`. Default
is `llvm`.

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions clang/include/clang/Basic/arm_sve.td
Original file line number Diff line number Diff line change
Expand Up @@ -1925,8 +1925,6 @@ def SVBGRP_N : SInst<"svbgrp[_n_{d}]", "dda", "UcUsUiUl", MergeNone, "aarch64_sv

let TargetGuard = "sve2p1" in {
def SVFCLAMP : SInst<"svclamp[_{d}]", "dddd", "hfd", MergeNone, "aarch64_sve_fclamp", [], []>;
def SVPTRUE_COUNT : SInst<"svptrue_{d}", "}v", "QcQsQiQl", MergeNone, "aarch64_sve_ptrue_{d}", [IsOverloadNone], []>;
def SVPFALSE_COUNT_ALIAS : SInst<"svpfalse_c", "}v", "", MergeNone, "", [IsOverloadNone]>;

def SVPEXT_SINGLE : SInst<"svpext_lane_{d}", "P}i", "QcQsQiQl", MergeNone, "aarch64_sve_pext", [], [ImmCheck<1, ImmCheck0_3>]>;
def SVPEXT_X2 : SInst<"svpext_lane_{d}_x2", "2.P}i", "QcQsQiQl", MergeNone, "aarch64_sve_pext_x2", [], [ImmCheck<1, ImmCheck0_1>]>;
Expand Down Expand Up @@ -2045,6 +2043,12 @@ def SVCNTP_COUNT : SInst<"svcntp_{d}", "n}i", "QcQsQiQl", MergeNone, "aarch64_sv
defm SVREVD : SInstZPZ<"svrevd", "csilUcUsUiUl", "aarch64_sve_revd">;
}

let TargetGuard = "sve2p1|sme2" in {
//FIXME: Replace IsStreamingCompatible with IsStreamingOrHasSVE2p1 when available
def SVPTRUE_COUNT : SInst<"svptrue_{d}", "}v", "QcQsQiQl", MergeNone, "aarch64_sve_ptrue_{d}", [IsOverloadNone, IsStreamingCompatible], []>;

def SVPFALSE_COUNT_ALIAS : SInst<"svpfalse_c", "}v", "", MergeNone, "", [IsOverloadNone, IsStreamingCompatible]>;
}

let TargetGuard = "sve2p1,b16b16" in {
defm SVMUL_BF : SInstZPZZ<"svmul", "b", "aarch64_sve_fmul", "aarch64_sve_fmul_u">;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class SerializablePathCollection {

/// Stores path to \p FE if it hasn't been stored yet.
/// \returns index to array exposed by getPathsBuffer().
size_t tryStoreFilePath(const clang::FileEntry &FE);
size_t tryStoreFilePath(FileEntryRef FE);

private:
/// Stores \p Path if it is non-empty.
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Format/TokenAnnotator.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ class AnnotatedLine {
return First->is(tok::comment) ? First->getNextNonComment() : First;
}

FormatToken *getLastNonComment() const {
assert(Last);
return Last->is(tok::comment) ? Last->getPreviousNonComment() : Last;
}

FormatToken *First;
FormatToken *Last;

Expand Down
19 changes: 6 additions & 13 deletions clang/lib/Format/UnwrappedLineFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,14 +346,10 @@ class LineJoiner {
return false;

// Check if the found line starts a record.
const FormatToken *LastNonComment = Line->Last;
const auto *LastNonComment = Line->getLastNonComment();
// There must be another token (usually `{`), because we chose a
// non-PPDirective and non-comment line that has a smaller level.
assert(LastNonComment);
if (LastNonComment->is(tok::comment)) {
LastNonComment = LastNonComment->getPreviousNonComment();
// There must be another token (usually `{`), because we chose a
// non-PPDirective and non-comment line that has a smaller level.
assert(LastNonComment);
}
return isRecordLBrace(*LastNonComment);
}
}
Expand All @@ -363,12 +359,9 @@ class LineJoiner {

bool MergeShortFunctions = ShouldMergeShortFunctions();

const FormatToken *FirstNonComment = TheLine->First;
if (FirstNonComment->is(tok::comment)) {
FirstNonComment = FirstNonComment->getNextNonComment();
if (!FirstNonComment)
return 0;
}
const auto *FirstNonComment = TheLine->getFirstNonComment();
if (!FirstNonComment)
return 0;
// FIXME: There are probably cases where we should use FirstNonComment
// instead of TheLine->First.

Expand Down
6 changes: 3 additions & 3 deletions clang/lib/IndexSerialization/SerializablePathCollection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,16 @@ SerializablePathCollection::SerializablePathCollection(
SysRootPath(Paths.addDirPath(SysRoot)),
OutputFilePath(Paths.addDirPath(OutputFile)) {}

size_t SerializablePathCollection::tryStoreFilePath(const FileEntry &FE) {
auto FileIt = UniqueFiles.find(&FE);
size_t SerializablePathCollection::tryStoreFilePath(FileEntryRef FE) {
auto FileIt = UniqueFiles.find(FE);
if (FileIt != UniqueFiles.end())
return FileIt->second;

const auto Dir = tryStoreDirPath(sys::path::parent_path(FE.getName()));
const auto FileIdx =
Paths.addFilePath(Dir.Root, Dir.Path, sys::path::filename(FE.getName()));

UniqueFiles.try_emplace(&FE, FileIdx);
UniqueFiles.try_emplace(FE, FileIdx);
return FileIdx;
}

Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2084,8 +2084,8 @@ void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) {
if (Ty->isSVESizelessBuiltinType() && FD && FD->hasBody()) {
llvm::StringMap<bool> CallerFeatureMap;
Context.getFunctionFeatureMap(CallerFeatureMap, FD);
if (!Builtin::evaluateRequiredTargetFeatures(
"sve", CallerFeatureMap))
if (!Builtin::evaluateRequiredTargetFeatures("sve", CallerFeatureMap) &&
!Builtin::evaluateRequiredTargetFeatures("sme", CallerFeatureMap))
Diag(D->getLocation(), diag::err_sve_vector_in_non_sve_target) << Ty;
}
};
Expand Down
17 changes: 6 additions & 11 deletions clang/test/CodeGen/aarch64-sve2p1-intrinsics/acle_sve2p1_pfalse.c
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// REQUIRES: aarch64-registered-target
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve2p1 -S -disable-O0-optnone -Werror -Wall -emit-llvm -o - %s | opt -S -passes=mem2reg,tailcallelim | FileCheck %s
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme2 -S -disable-O0-optnone -Werror -Wall -emit-llvm -o - %s | opt -S -passes=mem2reg,tailcallelim | FileCheck %s
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve2p1 -S -disable-O0-optnone -Werror -Wall -emit-llvm -o - -x c++ %s | opt -S -passes=mem2reg,tailcallelim | FileCheck %s -check-prefix=CPP-CHECK
// RUN: %clang_cc1 -DSVE_OVERLOADED_FORMS -triple aarch64-none-linux-gnu -target-feature +sve2p1 -S -disable-O0-optnone -Werror -Wall -emit-llvm -o - %s | opt -S -passes=mem2reg,tailcallelim | FileCheck %s
// RUN: %clang_cc1 -DSVE_OVERLOADED_FORMS -triple aarch64-none-linux-gnu -target-feature +sve2p1 -S -disable-O0-optnone -Werror -Wall -emit-llvm -o - -x c++ %s | opt -S -passes=mem2reg,tailcallelim | FileCheck %s -check-prefix=CPP-CHECK
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme2 -S -disable-O0-optnone -Werror -Wall -emit-llvm -o - -x c++ %s | opt -S -passes=mem2reg,tailcallelim | FileCheck %s -check-prefix=CPP-CHECK
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve2p1 -S -disable-O0-optnone -Werror -Wall -o /dev/null %s
#include <arm_sve.h>
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme2 -S -disable-O0-optnone -Werror -Wall -o /dev/null %s

#ifdef SVE_OVERLOADED_FORMS
// A simple used,unused... macro, long enough to represent any SVE builtin.
#define SVE_ACLE_FUNC(A1,A2_UNUSED,A3,A4_UNUSED) A1##A3
#else
#define SVE_ACLE_FUNC(A1,A2,A3,A4) A1##A2##A3##A4
#endif
#include <arm_sve.h>

// CHECK-LABEL: @test_svpfalse_c(
// CHECK-NEXT: entry:
Expand All @@ -24,7 +19,7 @@
// CPP-CHECK-NEXT: [[TMP0:%.*]] = tail call target("aarch64.svcount") @llvm.aarch64.sve.convert.from.svbool.taarch64.svcountt(<vscale x 16 x i1> zeroinitializer)
// CPP-CHECK-NEXT: ret target("aarch64.svcount") [[TMP0]]
//
svcount_t test_svpfalse_c()
svcount_t test_svpfalse_c(void) __arm_streaming_compatible
{
return SVE_ACLE_FUNC(svpfalse_c,,,)();
return svpfalse_c();
}
16 changes: 12 additions & 4 deletions clang/test/CodeGen/aarch64-sve2p1-intrinsics/acle_sve2p1_ptrue.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// REQUIRES: aarch64-registered-target
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve2p1 -S -O1 -Werror -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -DTEST_SME2 -triple aarch64-none-linux-gnu -target-feature +sme2 -S -O1 -Werror -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve2p1 -S -O1 -Werror -emit-llvm -o - -x c++ %s | FileCheck %s -check-prefix=CPP-CHECK
// RUN: %clang_cc1 -DTEST_SME2 -triple aarch64-none-linux-gnu -target-feature +sme2 -S -O1 -Werror -emit-llvm -o - -x c++ %s | FileCheck %s -check-prefix=CPP-CHECK

#include <arm_sve.h>

#ifndef TEST_SME2
#define ATTR
#else
#define ATTR __arm_streaming
#endif

// CHECK-LABEL: @test_svptrue_c8(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = tail call target("aarch64.svcount") @llvm.aarch64.sve.ptrue.c8()
Expand All @@ -15,7 +23,7 @@
// CPP-CHECK-NEXT: [[TMP0:%.*]] = tail call target("aarch64.svcount") @llvm.aarch64.sve.ptrue.c8()
// CPP-CHECK-NEXT: ret target("aarch64.svcount") [[TMP0]]
//
svcount_t test_svptrue_c8(void) {
svcount_t test_svptrue_c8(void) ATTR {
return svptrue_c8();
}

Expand All @@ -29,7 +37,7 @@ svcount_t test_svptrue_c8(void) {
// CPP-CHECK-NEXT: [[TMP0:%.*]] = tail call target("aarch64.svcount") @llvm.aarch64.sve.ptrue.c16()
// CPP-CHECK-NEXT: ret target("aarch64.svcount") [[TMP0]]
//
svcount_t test_svptrue_c16(void) {
svcount_t test_svptrue_c16(void) ATTR {
return svptrue_c16();
}

Expand All @@ -43,7 +51,7 @@ svcount_t test_svptrue_c16(void) {
// CPP-CHECK-NEXT: [[TMP0:%.*]] = tail call target("aarch64.svcount") @llvm.aarch64.sve.ptrue.c32()
// CPP-CHECK-NEXT: ret target("aarch64.svcount") [[TMP0]]
//
svcount_t test_svptrue_c32(void) {
svcount_t test_svptrue_c32(void) ATTR {
return svptrue_c32();
}

Expand All @@ -57,6 +65,6 @@ svcount_t test_svptrue_c32(void) {
// CPP-CHECK-NEXT: [[TMP0:%.*]] = tail call target("aarch64.svcount") @llvm.aarch64.sve.ptrue.c64()
// CPP-CHECK-NEXT: ret target("aarch64.svcount") [[TMP0]]
//
svcount_t test_svptrue_c64(void) {
svcount_t test_svptrue_c64(void) ATTR {
return svptrue_c64();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6327,7 +6327,7 @@ INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
VPrintf(1, "dlopen interceptor: DladdrSelfFName: %p %s\n",
(void *)SelfFName, SelfFName);

if (internal_strcmp(SelfFName, filename) == 0) {
if (SelfFName && internal_strcmp(SelfFName, filename) == 0) {
// It's possible they copied the string from dladdr, so
// we do a string comparison rather than pointer comparison.
VPrintf(1, "dlopen interceptor: replacing %s because it matches %s\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
// RUN: %clang -c -o %t.main.o %p/Inputs/dlopen-dlclose-x2.S
// RUN: %clang -c -o %t.inits.o %s
// RUN: %llvm_jitlink \
// RUN: -alias _dlopen=___orc_rt_macho_jit_dlopen \
// RUN: -alias _dlclose=___orc_rt_macho_jit_dlclose \
// RUN: -alias Platform:_dlopen=___orc_rt_macho_jit_dlopen \
// RUN: -alias Platform:_dlclose=___orc_rt_macho_jit_dlclose \
// RUN: %t.main.o -jd inits %t.inits.o -lmain | FileCheck %s

// CHECK: entering main
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
// RUN: %clang -c -o %t.main.o %p/Inputs/dlopen-dlclose-x2.S
// RUN: %clang -c -o %t.inits.o %s
// RUN: %llvm_jitlink \
// RUN: -alias _dlopen=___orc_rt_macho_jit_dlopen \
// RUN: -alias _dlclose=___orc_rt_macho_jit_dlclose \
// RUN: -alias Platform:_dlopen=___orc_rt_macho_jit_dlopen \
// RUN: -alias Platform:_dlclose=___orc_rt_macho_jit_dlclose \
// RUN: %t.main.o -jd inits %t.inits.o -lmain | FileCheck %s

// CHECK: entering main
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// RUN: %clang -c -o %t.inits.o %p/Inputs/standalone-ctor-and-cxa-atexit-dtor.S
// RUN: %clang -c -o %t.test.o %s
// RUN: %llvm_jitlink \
// RUN: -alias _dlopen=___orc_rt_macho_jit_dlopen \
// RUN: -alias _dlclose=___orc_rt_macho_jit_dlclose \
// RUN: -alias Platform:_dlopen=___orc_rt_macho_jit_dlopen \
// RUN: -alias Platform:_dlclose=___orc_rt_macho_jit_dlclose \
// RUN: %t.test.o -jd inits %t.inits.o -lmain | FileCheck %s

// CHECK: entering main
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// RUN: %clang -c -o %t.inits.o %p/Inputs/standalone-ctor-and-cxa-atexit-dtor.S
// RUN: %clang -c -o %t.test.o %s
// RUN: %llvm_jitlink \
// RUN: -alias _dlopen=___orc_rt_macho_jit_dlopen \
// RUN: -alias _dlclose=___orc_rt_macho_jit_dlclose \
// RUN: -alias Platform:_dlopen=___orc_rt_macho_jit_dlopen \
// RUN: -alias Platform:_dlclose=___orc_rt_macho_jit_dlclose \
// RUN: %t.test.o -jd inits %t.inits.o -lmain | FileCheck %s

// CHECK: entering main
Expand Down
6 changes: 6 additions & 0 deletions compiler-rt/test/orc/TestCases/Linux/ppc64/trivial-atexit.S
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
// Test that the runtime correctly interposes atexit.
//
// REQUIRES: disabled
// This test is disabled until a proper atexit interpose can be implemented:
// the current one assumes that atexit is defined in the dylib that calls it,
// which is not true in general. See
// https://github.com/llvm/llvm-project/issues/74641.
//
// RUN: %clang -c -o %t %s
// RUN: %llvm_jitlink %t

Expand Down
6 changes: 6 additions & 0 deletions compiler-rt/test/orc/TestCases/Linux/x86-64/trivial-atexit.S
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
// Test that the runtime correctly interposes atexit.
//
// REQUIRES: disabled
// This test is disabled until a proper atexit interpose can be implemented:
// the current one assumes that atexit is defined in the dylib that calls it,
// which is not true in general. See
// https://github.com/llvm/llvm-project/issues/74641
//
// RUN: %clang -c -o %t %s
// RUN: %llvm_jitlink %t

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// RUN: %clang_cl -MD -c -o %t.inits.o %p/Inputs/standalone-dylib.c
// RUN: %clang_cl -MD -c -o %t.test.o %s
// RUN: %llvm_jitlink \
// RUN: -alias dlopen=__orc_rt_coff_jit_dlopen \
// RUN: -alias dlclose=__orc_rt_coff_jit_dlclose \
// RUN: -alias Platform:dlopen=__orc_rt_coff_jit_dlopen \
// RUN: -alias Platform:dlclose=__orc_rt_coff_jit_dlclose \
// RUN: %t.test.o -jd inits %t.inits.o -lmain | FileCheck %s

// CHECK: entering main
Expand Down
6 changes: 4 additions & 2 deletions flang/test/Lower/default-initialization-globals.f90
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
! Test default initialization of global variables (static init)
! RUN: bbc -hlfir=false %s -o - | FileCheck %s
! RUN: bbc -hlfir=false %s -o - | FileCheck %s --check-prefixes=%if system-aix %{"CHECK","CHECK-BE"%} \
! RUN: %else %{"CHECK","CHECK-LE"%}

module tinit
real, target :: ziel(100)
Expand Down Expand Up @@ -191,7 +192,8 @@ subroutine eqv_same_default_init()
type(tseq), save :: somet1(2), somet2
equivalence (somet1(1), somet2)
! CHECK-LABEL: fir.global internal @_QFeqv_same_default_initEsomet1 : !fir.array<2xi64> {
! CHECK: %[[VAL_62:.*]] = arith.constant 12884901890 : i64
! CHECK-LE: %[[VAL_62:.*]] = arith.constant 12884901890 : i64
! CHECK-BE: %[[VAL_62:.*]] = arith.constant 8589934595 : i64
! CHECK: %[[VAL_63:.*]] = fir.undefined !fir.array<2xi64>
! CHECK: %[[VAL_64:.*]] = fir.insert_on_range %[[VAL_63]], %[[VAL_62]] from (0) to (1) : (!fir.array<2xi64>, i64) -> !fir.array<2xi64>
! CHECK: fir.has_value %[[VAL_64]] : !fir.array<2xi64>
Expand Down
51 changes: 9 additions & 42 deletions llvm/lib/CodeGen/RegisterCoalescer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,11 +305,7 @@ namespace {
/// number if it is not zero. If DstReg is a physical register and the
/// existing subregister number of the def / use being updated is not zero,
/// make sure to set it to the correct physical subregister.
///
/// If \p IsSubregToReg, we are coalescing a DstReg = SUBREG_TO_REG
/// SrcReg. This introduces an implicit-def of DstReg on coalesced users.
void updateRegDefsUses(Register SrcReg, Register DstReg, unsigned SubIdx,
bool IsSubregToReg);
void updateRegDefsUses(Register SrcReg, Register DstReg, unsigned SubIdx);

/// If the given machine operand reads only undefined lanes add an undef
/// flag.
Expand Down Expand Up @@ -1332,7 +1328,8 @@ bool RegisterCoalescer::reMaterializeTrivialDef(const CoalescerPair &CP,
if (DstReg.isPhysical()) {
Register NewDstReg = DstReg;

unsigned NewDstIdx = TRI->composeSubRegIndices(CP.getSrcIdx(), DefSubIdx);
unsigned NewDstIdx = TRI->composeSubRegIndices(CP.getSrcIdx(),
DefMI->getOperand(0).getSubReg());
if (NewDstIdx)
NewDstReg = TRI->getSubReg(DstReg, NewDstIdx);

Expand Down Expand Up @@ -1481,7 +1478,7 @@ bool RegisterCoalescer::reMaterializeTrivialDef(const CoalescerPair &CP,
MRI->setRegClass(DstReg, NewRC);

// Update machine operands and add flags.
updateRegDefsUses(DstReg, DstReg, DstIdx, false);
updateRegDefsUses(DstReg, DstReg, DstIdx);
NewMI.getOperand(0).setSubReg(NewIdx);
// updateRegDefUses can add an "undef" flag to the definition, since
// it will replace DstReg with DstReg.DstIdx. If NewIdx is 0, make
Expand Down Expand Up @@ -1803,7 +1800,7 @@ void RegisterCoalescer::addUndefFlag(const LiveInterval &Int, SlotIndex UseIdx,
}

void RegisterCoalescer::updateRegDefsUses(Register SrcReg, Register DstReg,
unsigned SubIdx, bool IsSubregToReg) {
unsigned SubIdx) {
bool DstIsPhys = DstReg.isPhysical();
LiveInterval *DstInt = DstIsPhys ? nullptr : &LIS->getInterval(DstReg);

Expand Down Expand Up @@ -1843,22 +1840,16 @@ void RegisterCoalescer::updateRegDefsUses(Register SrcReg, Register DstReg,
if (DstInt && !Reads && SubIdx && !UseMI->isDebugInstr())
Reads = DstInt->liveAt(LIS->getInstructionIndex(*UseMI));

bool FullDef = true;

// Replace SrcReg with DstReg in all UseMI operands.
for (unsigned i = 0, e = Ops.size(); i != e; ++i) {
MachineOperand &MO = UseMI->getOperand(Ops[i]);

// Adjust <undef> flags in case of sub-register joins. We don't want to
// turn a full def into a read-modify-write sub-register def and vice
// versa.
if (SubIdx && MO.isDef()) {
if (SubIdx && MO.isDef())
MO.setIsUndef(!Reads);

if (!Reads)
FullDef = false;
}

// A subreg use of a partially undef (super) register may be a complete
// undef use now and then has to be marked that way.
if (MO.isUse() && !DstIsPhys) {
Expand Down Expand Up @@ -1890,25 +1881,6 @@ void RegisterCoalescer::updateRegDefsUses(Register SrcReg, Register DstReg,
MO.substVirtReg(DstReg, SubIdx, *TRI);
}

if (IsSubregToReg && !FullDef) {
// If the coalesed instruction doesn't fully define the register, we need
// to preserve the original super register liveness for SUBREG_TO_REG.
//
// We pretended SUBREG_TO_REG was a regular copy for coalescing purposes,
// but it introduces liveness for other subregisters. Downstream users may
// have been relying on those bits, so we need to ensure their liveness is
// captured with a def of other lanes.

// FIXME: Need to add new subrange if tracking subranges. We could also
// skip adding this if we knew the other lanes are dead, and only for
// other lanes.

assert(!MRI->shouldTrackSubRegLiveness(DstReg) &&
"this should update subranges");
MachineInstrBuilder MIB(*MF, UseMI);
MIB.addReg(DstReg, RegState::ImplicitDefine);
}

LLVM_DEBUG({
dbgs() << "\t\tupdated: ";
if (!UseMI->isDebugInstr())
Expand Down Expand Up @@ -2108,8 +2080,6 @@ bool RegisterCoalescer::joinCopy(MachineInstr *CopyMI, bool &Again) {
});
}

const bool IsSubregToReg = CopyMI->isSubregToReg();

ShrinkMask = LaneBitmask::getNone();
ShrinkMainRange = false;

Expand Down Expand Up @@ -2177,12 +2147,9 @@ bool RegisterCoalescer::joinCopy(MachineInstr *CopyMI, bool &Again) {

// Rewrite all SrcReg operands to DstReg.
// Also update DstReg operands to include DstIdx if it is set.
if (CP.getDstIdx()) {
assert(!IsSubregToReg && "can this happen?");
updateRegDefsUses(CP.getDstReg(), CP.getDstReg(), CP.getDstIdx(), false);
}
updateRegDefsUses(CP.getSrcReg(), CP.getDstReg(), CP.getSrcIdx(),
IsSubregToReg);
if (CP.getDstIdx())
updateRegDefsUses(CP.getDstReg(), CP.getDstReg(), CP.getDstIdx());
updateRegDefsUses(CP.getSrcReg(), CP.getDstReg(), CP.getSrcIdx());

// Shrink subregister ranges if necessary.
if (ShrinkMask.any()) {
Expand Down
21 changes: 18 additions & 3 deletions llvm/lib/Transforms/IPO/LowerTypeTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1467,9 +1467,19 @@ void LowerTypeTestsModule::createJumpTable(
SmallVector<Value *, 16> AsmArgs;
AsmArgs.reserve(Functions.size() * 2);

for (GlobalTypeMember *GTM : Functions)
// Check if all entries have the NoUnwind attribute.
// If all entries have it, we can safely mark the
// cfi.jumptable as NoUnwind, otherwise, direct calls
// to the jump table will not handle exceptions properly
bool areAllEntriesNounwind = true;
for (GlobalTypeMember *GTM : Functions) {
if (!llvm::cast<llvm::Function>(GTM->getGlobal())
->hasFnAttribute(llvm::Attribute::NoUnwind)) {
areAllEntriesNounwind = false;
}
createJumpTableEntry(AsmOS, ConstraintOS, JumpTableArch, AsmArgs,
cast<Function>(GTM->getGlobal()));
}

// Align the whole table by entry size.
F->setAlignment(Align(getJumpTableEntrySize()));
Expand Down Expand Up @@ -1512,8 +1522,13 @@ void LowerTypeTestsModule::createJumpTable(
// -fcf-protection=.
if (JumpTableArch == Triple::x86 || JumpTableArch == Triple::x86_64)
F->addFnAttr(Attribute::NoCfCheck);
// Make sure we don't emit .eh_frame for this function.
F->addFnAttr(Attribute::NoUnwind);

// Make sure we don't emit .eh_frame for this function if it isn't needed.
if (areAllEntriesNounwind)
F->addFnAttr(Attribute::NoUnwind);

// Make sure we do not inline any calls to the cfi.jumptable.
F->addFnAttr(Attribute::NoInline);

BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", F);
IRBuilder<> IRB(BB);
Expand Down
92 changes: 46 additions & 46 deletions llvm/test/CodeGen/AArch64/GlobalISel/arm64-pcsections.ll

Large diffs are not rendered by default.

185 changes: 0 additions & 185 deletions llvm/test/CodeGen/X86/coalescer-breaks-subreg-to-reg-liveness.ll

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ body: |
; CHECK-NEXT: successors: %bb.1(0x2aaaaaab), %bb.2(0x55555555)
; CHECK-NEXT: liveins: $edi
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: undef [[MOV32r0_:%[0-9]+]].sub_32bit:gr64_with_sub_8bit = MOV32r0 implicit-def dead $eflags, implicit-def [[MOV32r0_]]
; CHECK-NEXT: undef [[MOV32r0_:%[0-9]+]].sub_32bit:gr64_with_sub_8bit = MOV32r0 implicit-def dead $eflags
; CHECK-NEXT: JCC_1 %bb.2, 5, implicit killed undef $eflags
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1:
Expand All @@ -28,7 +28,7 @@ body: |
; CHECK-NEXT: JCC_1 %bb.5, 5, implicit killed undef $eflags
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.4:
; CHECK-NEXT: dead $eax = MOV32r0 implicit-def dead $eflags, implicit-def $al, implicit-def $al
; CHECK-NEXT: dead $eax = MOV32r0 implicit-def dead $eflags, implicit-def $al
; CHECK-NEXT: RET 0, killed undef $al
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.5:
Expand Down

This file was deleted.

348 changes: 0 additions & 348 deletions llvm/test/CodeGen/X86/subreg-to-reg-coalescing.mir

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

// Check that main is a thumb symbol (with LSB set) and printf is arm (with LSB clear)
//
// CHECK-LABEL: Symbol table:
// CHECK-LABEL: JITDylib "main"
// CHECK-NEXT: Link order: [ ("main", MatchAllSymbols), ("Process", MatchExportedSymbolsOnly) ]
// CHECK-NEXT: Symbol table:
// CHECK-NEXT: "main": 0x{{[0-9a-f]+[13579bdf]}} [Callable] Ready
// CHECK-NEXT: "printf": 0x76bbe880 [Data] Ready

Expand Down
35 changes: 27 additions & 8 deletions llvm/test/Transforms/LowerTypeTests/aarch64-jumptable.ll
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 2
; RUN: opt -S -passes=lowertypetests -mtriple=aarch64-unknown-linux-gnu %s | FileCheck --check-prefixes=AARCH64 %s

; Test for the jump table generation with branch protection on AArch64
Expand All @@ -6,7 +7,6 @@ target datalayout = "e-p:64:64"

@0 = private unnamed_addr constant [2 x ptr] [ptr @f, ptr @g], align 16

; AARCH64: @f = alias void (), ptr @[[JT:.*]]

define void @f() !type !0 {
ret void
Expand All @@ -29,11 +29,30 @@ define i1 @foo(ptr %p) {

!1 = !{i32 4, !"branch-target-enforcement", i32 1}

; AARCH64: define private void @[[JT]]() #[[ATTR:.*]] align 8 {

; AARCH64: bti c
; AARCH64-SAME: b $0
; AARCH64-SAME: bti c
; AARCH64-SAME: b $1

; AARCH64: attributes #[[ATTR]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none"
; AARCH64-LABEL: define hidden void @f.cfi() !type !1 {
; AARCH64-NEXT: ret void
;
;
; AARCH64-LABEL: define internal void @g.cfi() !type !1 {
; AARCH64-NEXT: ret void
;
;
; AARCH64-LABEL: define i1 @foo
; AARCH64-SAME: (ptr [[P:%.*]]) {
; AARCH64-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
; AARCH64-NEXT: [[TMP2:%.*]] = sub i64 [[TMP1]], ptrtoint (ptr @.cfi.jumptable to i64)
; AARCH64-NEXT: [[TMP3:%.*]] = lshr i64 [[TMP2]], 3
; AARCH64-NEXT: [[TMP4:%.*]] = shl i64 [[TMP2]], 61
; AARCH64-NEXT: [[TMP5:%.*]] = or i64 [[TMP3]], [[TMP4]]
; AARCH64-NEXT: [[TMP6:%.*]] = icmp ule i64 [[TMP5]], 1
; AARCH64-NEXT: ret i1 [[TMP6]]
;
;
; AARCH64: Function Attrs: naked noinline
; AARCH64-LABEL: define private void @.cfi.jumptable
; AARCH64-SAME: () #[[ATTR1:[0-9]+]] align 8 {
; AARCH64-NEXT: entry:
; AARCH64-NEXT: call void asm sideeffect "bti c\0Ab $0\0Abti c\0Ab $1\0A", "s,s"(ptr @f.cfi, ptr @g.cfi)
; AARCH64-NEXT: unreachable
;
160 changes: 160 additions & 0 deletions llvm/test/Transforms/LowerTypeTests/cfi-nounwind-direct-call.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 2
; RUN: opt < %s -passes='lowertypetests,default<O3>' -S | FileCheck %s

; This IR is based of the following C++
; which was compiled with:
; clang -cc1 -fexceptions -fcxx-exceptions \
; -std=c++11 -internal-isystem llvm-project/build/lib/clang/17/include \
; -nostdsysteminc -triple x86_64-unknown-linux -fsanitize=cfi-icall \
; -fsanitize-cfi-cross-dso -fsanitize-trap=cfi-icall -Oz -S -emit-llvm
; int (*catch_ptr)(int);
; int nothrow_e (int num) noexcept {
; if (num) return 1;
; return 0;
; }
; int call_catch(int num) {
; catch_ptr = &nothrow_e;
; return catch_ptr(num);
; }

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux"

@catch_ptr = local_unnamed_addr global ptr null, align 8
@llvm.used = appending global [1 x ptr] [ptr @__cfi_check_fail], section "llvm.metadata"

; Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none)
define dso_local noundef i32 @_Z9nothrow_ei(i32 noundef %num) #0 !type !4 !type !5 !type !6 {
entry:
%tobool.not = icmp ne i32 %num, 0
%. = zext i1 %tobool.not to i32
ret i32 %.
}

; Function Attrs: minsize mustprogress nounwind optsize
define dso_local noundef i32 @_Z10call_catchi(i32 noundef %num) local_unnamed_addr #1 !type !4 !type !5 !type !6 {
entry:
store ptr @_Z9nothrow_ei, ptr @catch_ptr, align 8, !tbaa !7
%0 = tail call i1 @llvm.type.test(ptr nonnull @_Z9nothrow_ei, metadata !"_ZTSFiiE"), !nosanitize !11
br i1 %0, label %cfi.cont, label %cfi.slowpath, !prof !12, !nosanitize !11

cfi.slowpath: ; preds = %entry
tail call void @__cfi_slowpath(i64 5174074510188755522, ptr nonnull @_Z9nothrow_ei) #5, !nosanitize !11
br label %cfi.cont, !nosanitize !11

cfi.cont: ; preds = %cfi.slowpath, %entry
%tobool.not.i = icmp ne i32 %num, 0
%..i = zext i1 %tobool.not.i to i32
ret i32 %..i
}

; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i1 @llvm.type.test(ptr, metadata) #2

declare void @__cfi_slowpath(i64, ptr) local_unnamed_addr

; Function Attrs: minsize optsize
define weak_odr hidden void @__cfi_check_fail(ptr noundef %0, ptr noundef %1) #3 {
entry:
%.not = icmp eq ptr %0, null, !nosanitize !11
br i1 %.not, label %trap, label %cont, !nosanitize !11

trap: ; preds = %cont, %entry
tail call void @llvm.ubsantrap(i8 2) #6, !nosanitize !11
unreachable, !nosanitize !11

cont: ; preds = %entry
%2 = load i8, ptr %0, align 4, !nosanitize !11
%switch = icmp ult i8 %2, 5
br i1 %switch, label %trap, label %cont6

cont6: ; preds = %cont
ret void, !nosanitize !11
}

; Function Attrs: cold noreturn nounwind
declare void @llvm.ubsantrap(i8 immarg) #4

define weak void @__cfi_check(i64 %0, ptr %1, ptr %2) local_unnamed_addr {
entry:
tail call void @llvm.trap()
unreachable
}

; Function Attrs: cold noreturn nounwind
declare void @llvm.trap() #4

attributes #0 = { minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
attributes #1 = { minsize mustprogress nounwind optsize "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
attributes #2 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #3 = { minsize optsize "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
attributes #4 = { cold noreturn nounwind }
attributes #5 = { nounwind }
attributes #6 = { noreturn nounwind }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"Cross-DSO CFI", i32 1}
!2 = !{i32 4, !"CFI Canonical Jump Tables", i32 0}
!3 = !{!"clang version 17.0.2"}
!4 = !{i64 0, !"_ZTSFiiE"}
!5 = !{i64 0, !"_ZTSFiiE.generalized"}
!6 = !{i64 0, i64 5174074510188755522}
!7 = !{!8, !8, i64 0}
!8 = !{!"any pointer", !9, i64 0}
!9 = !{!"omnipotent char", !10, i64 0}
!10 = !{!"Simple C++ TBAA"}
!11 = !{}
!12 = !{!"branch_weights", i32 1048575, i32 1}
; CHECK: Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(none)
; CHECK-LABEL: define dso_local noundef i32 @_Z9nothrow_ei
; CHECK-SAME: (i32 noundef [[NUM:%.*]]) #[[ATTR0:[0-9]+]] !type !4 !type !5 !type !6 {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp ne i32 [[NUM]], 0
; CHECK-NEXT: [[DOT:%.*]] = zext i1 [[TOBOOL_NOT]] to i32
; CHECK-NEXT: ret i32 [[DOT]]
;
;
; CHECK: Function Attrs: minsize mustprogress nofree norecurse nosync nounwind optsize willreturn memory(write, argmem: none, inaccessiblemem: none)
; CHECK-LABEL: define dso_local noundef i32 @_Z10call_catchi
; CHECK-SAME: (i32 noundef [[NUM:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] !type !4 !type !5 !type !6 {
; CHECK-NEXT: entry:
; CHECK-NEXT: store ptr @_Z9nothrow_ei.cfi_jt, ptr @catch_ptr, align 8, !tbaa [[TBAA7:![0-9]+]]
; CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp ne i32 [[NUM]], 0
; CHECK-NEXT: [[DOT_I:%.*]] = zext i1 [[TOBOOL_NOT_I]] to i32
; CHECK-NEXT: ret i32 [[DOT_I]]
;
;
; CHECK: Function Attrs: minsize optsize
; CHECK-LABEL: define weak_odr hidden void @__cfi_check_fail
; CHECK-SAME: (ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]]) #[[ATTR2:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[TMP0]], null, !nosanitize !11
; CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !nosanitize !11
; CHECK: trap:
; CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR5:[0-9]+]], !nosanitize !11
; CHECK-NEXT: unreachable, !nosanitize !11
; CHECK: cont:
; CHECK-NEXT: [[TMP2:%.*]] = load i8, ptr [[TMP0]], align 4, !nosanitize !11
; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i8 [[TMP2]], 5
; CHECK-NEXT: br i1 [[SWITCH]], label [[TRAP]], label [[CONT6:%.*]]
; CHECK: cont6:
; CHECK-NEXT: ret void, !nosanitize !11
;
;
; CHECK-LABEL: define weak void @__cfi_check
; CHECK-SAME: (i64 [[TMP0:%.*]], ptr [[TMP1:%.*]], ptr [[TMP2:%.*]]) local_unnamed_addr {
; CHECK-NEXT: entry:
; CHECK-NEXT: tail call void @llvm.trap()
; CHECK-NEXT: unreachable
;
;
; CHECK: Function Attrs: naked nocf_check noinline nounwind
; CHECK-LABEL: define internal void @_Z9nothrow_ei.cfi_jt
; CHECK-SAME: () #[[ATTR4:[0-9]+]] align 8 {
; CHECK-NEXT: entry:
; CHECK-NEXT: tail call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr nonnull @_Z9nothrow_ei) #[[ATTR6:[0-9]+]]
; CHECK-NEXT: unreachable
;
228 changes: 228 additions & 0 deletions llvm/test/Transforms/LowerTypeTests/cfi-unwind-direct-call.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --include-generated-funcs --version 2
; RUN: opt < %s -passes='lowertypetests,default<O3>' -S | FileCheck %s

; This IR is based of the following C++
; which was compiled with:
; clang -cc1 -fexceptions -fcxx-exceptions \
; -std=c++11 -internal-isystem llvm-project/build/lib/clang/17/include \
; -nostdsysteminc -triple x86_64-unknown-linux -fsanitize=cfi-icall \
; -fsanitize-cfi-cross-dso -fsanitize-trap=cfi-icall -Oz -S -emit-llvm
; void (*catch_ptr)(int);
; void throw_e (int num) {
; if (num) throw 20;
; }
; void call_catch(int num) {
; catch_ptr = &throw_e;
; try{
; catch_ptr(num);
; } catch (int i) {
; }
; }

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux"

@catch_ptr = local_unnamed_addr global ptr null, align 8
@_ZTIi = external constant ptr
@llvm.used = appending global [1 x ptr] [ptr @__cfi_check_fail], section "llvm.metadata"

; Function Attrs: minsize mustprogress optsize
define dso_local void @_Z7throw_ei(i32 noundef %num) #0 !type !4 !type !5 !type !6 {
entry:
%tobool.not = icmp eq i32 %num, 0
br i1 %tobool.not, label %if.end, label %if.then

if.then: ; preds = %entry
%exception = tail call ptr @__cxa_allocate_exception(i64 4) #5
store i32 20, ptr %exception, align 16, !tbaa !7
tail call void @__cxa_throw(ptr nonnull %exception, ptr nonnull @_ZTIi, ptr null) #6
unreachable

if.end: ; preds = %entry
ret void
}

declare ptr @__cxa_allocate_exception(i64) local_unnamed_addr

declare void @__cxa_throw(ptr, ptr, ptr) local_unnamed_addr

; Function Attrs: minsize mustprogress optsize
define dso_local void @_Z10call_catchi(i32 noundef %num) local_unnamed_addr #0 personality ptr @__gxx_personality_v0 !type !4 !type !5 !type !6 {
entry:
store ptr @_Z7throw_ei, ptr @catch_ptr, align 8, !tbaa !11
%0 = tail call i1 @llvm.type.test(ptr nonnull @_Z7throw_ei, metadata !"_ZTSFviE"), !nosanitize !13
br i1 %0, label %cfi.cont, label %cfi.slowpath, !prof !14, !nosanitize !13

cfi.slowpath: ; preds = %entry
tail call void @__cfi_slowpath(i64 -8738933900360652027, ptr nonnull @_Z7throw_ei) #5, !nosanitize !13
br label %cfi.cont, !nosanitize !13

cfi.cont: ; preds = %cfi.slowpath, %entry
invoke void @_Z7throw_ei(i32 noundef %num) #7
to label %try.cont unwind label %lpad

lpad: ; preds = %cfi.cont
%1 = landingpad { ptr, i32 }
catch ptr @_ZTIi
%2 = extractvalue { ptr, i32 } %1, 1
%3 = tail call i32 @llvm.eh.typeid.for(ptr nonnull @_ZTIi) #5
%matches = icmp eq i32 %2, %3
br i1 %matches, label %catch, label %eh.resume

catch: ; preds = %lpad
%4 = extractvalue { ptr, i32 } %1, 0
%5 = tail call ptr @__cxa_begin_catch(ptr %4) #5
tail call void @__cxa_end_catch() #5
br label %try.cont

try.cont: ; preds = %cfi.cont, %catch
ret void

eh.resume: ; preds = %lpad
resume { ptr, i32 } %1
}

; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i1 @llvm.type.test(ptr, metadata) #1

declare void @__cfi_slowpath(i64, ptr) local_unnamed_addr

declare i32 @__gxx_personality_v0(...)

; Function Attrs: nofree nosync nounwind memory(none)
declare i32 @llvm.eh.typeid.for(ptr) #2

declare ptr @__cxa_begin_catch(ptr) local_unnamed_addr

declare void @__cxa_end_catch() local_unnamed_addr

; Function Attrs: minsize optsize
define weak_odr hidden void @__cfi_check_fail(ptr noundef %0, ptr noundef %1) #3 {
entry:
%.not = icmp eq ptr %0, null, !nosanitize !13
br i1 %.not, label %trap, label %cont, !nosanitize !13

trap: ; preds = %cont, %entry
tail call void @llvm.ubsantrap(i8 2) #8, !nosanitize !13
unreachable, !nosanitize !13

cont: ; preds = %entry
%2 = load i8, ptr %0, align 4, !nosanitize !13
%switch = icmp ult i8 %2, 5
br i1 %switch, label %trap, label %cont6

cont6: ; preds = %cont
ret void, !nosanitize !13
}

; Function Attrs: cold noreturn nounwind
declare void @llvm.ubsantrap(i8 immarg) #4

define weak void @__cfi_check(i64 %0, ptr %1, ptr %2) local_unnamed_addr {
entry:
tail call void @llvm.trap()
unreachable
}

; Function Attrs: cold noreturn nounwind
declare void @llvm.trap() #4

attributes #0 = { minsize mustprogress optsize "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
attributes #1 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #2 = { nofree nosync nounwind memory(none) }
attributes #3 = { minsize optsize "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
attributes #4 = { cold noreturn nounwind }
attributes #5 = { nounwind }
attributes #6 = { noreturn }
attributes #7 = { minsize optsize }
attributes #8 = { noreturn nounwind }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"Cross-DSO CFI", i32 1}
!2 = !{i32 4, !"CFI Canonical Jump Tables", i32 0}
!3 = !{!"clang version 17.0.2"}
!4 = !{i64 0, !"_ZTSFviE"}
!5 = !{i64 0, !"_ZTSFviE.generalized"}
!6 = !{i64 0, i64 -8738933900360652027}
!7 = !{!8, !8, i64 0}
!8 = !{!"int", !9, i64 0}
!9 = !{!"omnipotent char", !10, i64 0}
!10 = !{!"Simple C++ TBAA"}
!11 = !{!12, !12, i64 0}
!12 = !{!"any pointer", !9, i64 0}
!13 = !{}
!14 = !{!"branch_weights", i32 1048575, i32 1}
; CHECK: Function Attrs: minsize mustprogress optsize
; CHECK-LABEL: define dso_local void @_Z7throw_ei
; CHECK-SAME: (i32 noundef [[NUM:%.*]]) #[[ATTR0:[0-9]+]] !type !4 !type !5 !type !6 {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[NUM]], 0
; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[EXCEPTION:%.*]] = tail call ptr @__cxa_allocate_exception(i64 4) #[[ATTR5:[0-9]+]]
; CHECK-NEXT: store i32 20, ptr [[EXCEPTION]], align 16, !tbaa [[TBAA7:![0-9]+]]
; CHECK-NEXT: tail call void @__cxa_throw(ptr nonnull [[EXCEPTION]], ptr nonnull @_ZTIi, ptr null) #[[ATTR6:[0-9]+]]
; CHECK-NEXT: unreachable
; CHECK: if.end:
; CHECK-NEXT: ret void
;
;
; CHECK: Function Attrs: minsize mustprogress optsize
; CHECK-LABEL: define dso_local void @_Z10call_catchi
; CHECK-SAME: (i32 noundef [[NUM:%.*]]) local_unnamed_addr #[[ATTR0]] personality ptr @__gxx_personality_v0 !type !4 !type !5 !type !6 {
; CHECK-NEXT: entry:
; CHECK-NEXT: store ptr @_Z7throw_ei.cfi_jt, ptr @catch_ptr, align 8, !tbaa [[TBAA11:![0-9]+]]
; CHECK-NEXT: invoke void @_Z7throw_ei.cfi_jt() #[[ATTR7:[0-9]+]]
; CHECK-NEXT: to label [[TRY_CONT:%.*]] unwind label [[LPAD:%.*]]
; CHECK: lpad:
; CHECK-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: catch ptr @_ZTIi
; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 1
; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @llvm.eh.typeid.for(ptr nonnull @_ZTIi) #[[ATTR5]]
; CHECK-NEXT: [[MATCHES:%.*]] = icmp eq i32 [[TMP1]], [[TMP2]]
; CHECK-NEXT: br i1 [[MATCHES]], label [[CATCH:%.*]], label [[EH_RESUME:%.*]]
; CHECK: catch:
; CHECK-NEXT: [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 0
; CHECK-NEXT: [[TMP4:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP3]]) #[[ATTR5]]
; CHECK-NEXT: tail call void @__cxa_end_catch() #[[ATTR5]]
; CHECK-NEXT: br label [[TRY_CONT]]
; CHECK: try.cont:
; CHECK-NEXT: ret void
; CHECK: eh.resume:
; CHECK-NEXT: resume { ptr, i32 } [[TMP0]]
;
;
; CHECK: Function Attrs: minsize optsize
; CHECK-LABEL: define weak_odr hidden void @__cfi_check_fail
; CHECK-SAME: (ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]]) #[[ATTR2:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[TMP0]], null, !nosanitize !13
; CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !nosanitize !13
; CHECK: trap:
; CHECK-NEXT: tail call void @llvm.ubsantrap(i8 2) #[[ATTR8:[0-9]+]], !nosanitize !13
; CHECK-NEXT: unreachable, !nosanitize !13
; CHECK: cont:
; CHECK-NEXT: [[TMP2:%.*]] = load i8, ptr [[TMP0]], align 4, !nosanitize !13
; CHECK-NEXT: [[SWITCH:%.*]] = icmp ult i8 [[TMP2]], 5
; CHECK-NEXT: br i1 [[SWITCH]], label [[TRAP]], label [[CONT6:%.*]]
; CHECK: cont6:
; CHECK-NEXT: ret void, !nosanitize !13
;
;
; CHECK-LABEL: define weak void @__cfi_check
; CHECK-SAME: (i64 [[TMP0:%.*]], ptr [[TMP1:%.*]], ptr [[TMP2:%.*]]) local_unnamed_addr {
; CHECK-NEXT: entry:
; CHECK-NEXT: tail call void @llvm.trap()
; CHECK-NEXT: unreachable
;
;
; CHECK: Function Attrs: naked nocf_check noinline
; CHECK-LABEL: define internal void @_Z7throw_ei.cfi_jt
; CHECK-SAME: () #[[ATTR4:[0-9]+]] align 8 {
; CHECK-NEXT: entry:
; CHECK-NEXT: tail call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr nonnull @_Z7throw_ei) #[[ATTR5]]
; CHECK-NEXT: unreachable
;
4 changes: 2 additions & 2 deletions llvm/test/Transforms/LowerTypeTests/function-arm-thumb.ll
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ define void @addrtaken() {
; CHECK-NEXT: unreachable
; CHECK-NEXT: }

; CHECK-DAG: attributes #[[AA]] = { naked nounwind "target-features"="-thumb-mode" }
; CHECK-DAG: attributes #[[AT]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
; CHECK-DAG: attributes #[[AA]] = { naked noinline "target-features"="-thumb-mode" }
; CHECK-DAG: attributes #[[AT]] = { naked noinline "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
4 changes: 2 additions & 2 deletions llvm/test/Transforms/LowerTypeTests/function-thumb-bti.ll
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ define i1 @foo(ptr %p) {
; BTI: call void asm sideeffect "bti\0Ab.w $0\0Abti\0Ab.w $1\0A", "s,s"(ptr @f.cfi, ptr @g.cfi)
; NOBTI: call void asm sideeffect "b.w $0\0Ab.w $1\0A", "s,s"(ptr @f.cfi, ptr @g.cfi)

; BTI: attributes [[ATTRS]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none" "target-features"="+thumb-mode,+pacbti" }
; NOBTI: attributes [[ATTRS]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
; BTI: attributes [[ATTRS]] = { naked noinline "branch-target-enforcement"="false" "sign-return-address"="none" "target-features"="+thumb-mode,+pacbti" }
; NOBTI: attributes [[ATTRS]] = { naked noinline "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
16 changes: 8 additions & 8 deletions llvm/test/Transforms/LowerTypeTests/function.ll
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ define internal void @g() !type !0 {

!0 = !{i32 0, !"typeid1"}

declare i1 @llvm.type.test(ptr %ptr, metadata %bitset) nounwind readnone
declare i1 @llvm.type.test(ptr %ptr, metadata %bitset) noinline readnone

define i1 @foo(ptr %p) {
; NATIVE: sub i64 {{.*}}, ptrtoint (ptr @[[JT]] to i64)
Expand Down Expand Up @@ -109,13 +109,13 @@ define i1 @foo(ptr %p) {

; NATIVE-SAME: "s,s"(ptr @f.cfi, ptr @g.cfi)

; X86-LINUX: attributes #[[ATTR]] = { naked nocf_check nounwind }
; X86-WIN32: attributes #[[ATTR]] = { nocf_check nounwind }
; ARM: attributes #[[ATTR]] = { naked nounwind
; THUMB: attributes #[[ATTR]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
; THUMBV6M: attributes #[[ATTR]] = { naked nounwind "branch-target-enforcement"="false" "sign-return-address"="none" "target-features"="+thumb-mode" }
; RISCV: attributes #[[ATTR]] = { naked nounwind "target-features"="-c,-relax" }
; LOONGARCH64: attributes #[[ATTR]] = { naked nounwind }
; X86-LINUX: attributes #[[ATTR]] = { naked nocf_check noinline }
; X86-WIN32: attributes #[[ATTR]] = { nocf_check noinline }
; ARM: attributes #[[ATTR]] = { naked noinline
; THUMB: attributes #[[ATTR]] = { naked noinline "branch-target-enforcement"="false" "sign-return-address"="none" "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
; THUMBV6M: attributes #[[ATTR]] = { naked noinline "branch-target-enforcement"="false" "sign-return-address"="none" "target-features"="+thumb-mode" }
; RISCV: attributes #[[ATTR]] = { naked noinline "target-features"="-c,-relax" }
; LOONGARCH64: attributes #[[ATTR]] = { naked noinline }

; WASM32: ![[I0]] = !{i64 1}
; WASM32: ![[I1]] = !{i64 2}
2 changes: 1 addition & 1 deletion llvm/test/Transforms/LowerTypeTests/x86-jumptable.ll
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ define i1 @foo(ptr %p) {
; X86_32-NEXT: call void asm sideeffect "endbr32\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0Aendbr32\0Ajmp ${1:c}@plt\0A.balign 16, 0xcc\0A", "s,s"(ptr @f.cfi, ptr @g.cfi)
; X86_64-NEXT: call void asm sideeffect "endbr64\0Ajmp ${0:c}@plt\0A.balign 16, 0xcc\0Aendbr64\0Ajmp ${1:c}@plt\0A.balign 16, 0xcc\0A", "s,s"(ptr @f.cfi, ptr @g.cfi)

; X86_64: attributes #[[#ATTR]] = { naked nocf_check nounwind }
; X86_64: attributes #[[#ATTR]] = { naked nocf_check noinline }
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# RUN: not llvm-exegesis -mtriple=x86_64-unknown-unknown -snippets-file=%s -mode=latency 2>&1 | FileCheck %s

# CHECK: llvm-exegesis error: Memory and snippet address annotations are only supported in subprocess execution mode
# CHECK: llvm-exegesis error: Memory annotations are only supported in subprocess execution mode

# LLVM-EXEGESIS-MEM-DEF test1 4096 ff

Expand Down

This file was deleted.

This file was deleted.

3 changes: 0 additions & 3 deletions llvm/tools/llvm-exegesis/lib/BenchmarkResult.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ struct BenchmarkKey {
// An opaque configuration, that can be used to separate several benchmarks of
// the same instruction under different configurations.
std::string Config;
// The address that the snippet should be loaded in at if the execution mode
// being used supports it.
intptr_t SnippetAddress = 0;
};

struct BenchmarkMeasure {
Expand Down
12 changes: 2 additions & 10 deletions llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,17 +416,9 @@ class SubProcessFunctionExecutorImpl
#endif // GLIBC_INITS_RSEQ

size_t FunctionDataCopySize = this->Function.FunctionBytes.size();
void *MapAddress = NULL;
int MapFlags = MAP_PRIVATE | MAP_ANONYMOUS;

if (Key.SnippetAddress != 0) {
MapAddress = reinterpret_cast<void *>(Key.SnippetAddress);
MapFlags |= MAP_FIXED_NOREPLACE;
}

char *FunctionDataCopy =
(char *)mmap(MapAddress, FunctionDataCopySize, PROT_READ | PROT_WRITE,
MapFlags, 0, 0);
(char *)mmap(NULL, FunctionDataCopySize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if ((intptr_t)FunctionDataCopy == -1)
exit(ChildProcessExitCodeE::FunctionDataMappingFailed);

Expand Down
13 changes: 0 additions & 13 deletions llvm/tools/llvm-exegesis/lib/SnippetFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,6 @@ class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer {
Result->Key.MemoryMappings.push_back(std::move(MemMap));
return;
}
if (CommentText.consume_front("SNIPPET-ADDRESS")) {
// LLVM-EXEGESIS-SNIPPET-ADDRESS <address>
if (!to_integer<intptr_t>(CommentText.trim(), Result->Key.SnippetAddress,
16)) {
errs() << "invalid comment 'LLVM-EXEGESIS-SNIPPET-ADDRESS "
<< CommentText
<< "', expected <ADDRESS> to contain a valid integer in "
"hexadecimal format";
++InvalidComments;
return;
}
return;
}
}

unsigned numInvalidComments() const { return InvalidComments; }
Expand Down
6 changes: 2 additions & 4 deletions llvm/tools/llvm-exegesis/llvm-exegesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -533,10 +533,8 @@ void benchmarkMain() {
for (const auto &Configuration : Configurations) {
if (ExecutionMode != BenchmarkRunner::ExecutionModeE::SubProcess &&
(Configuration.Key.MemoryMappings.size() != 0 ||
Configuration.Key.MemoryValues.size() != 0 ||
Configuration.Key.SnippetAddress != 0))
ExitWithError("Memory and snippet address annotations are only "
"supported in subprocess "
Configuration.Key.MemoryValues.size() != 0))
ExitWithError("Memory annotations are only supported in subprocess "
"execution mode");
}
}
Expand Down
221 changes: 157 additions & 64 deletions llvm/tools/llvm-jitlink/llvm-jitlink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,11 +692,12 @@ getTestObjectFileInterface(Session &S, MemoryBufferRef O) {
}

static Error loadProcessSymbols(Session &S) {
S.ProcessSymsJD = &S.ES.createBareJITDylib("Process");
auto FilterMainEntryPoint =
[EPName = S.ES.intern(EntryPointName)](SymbolStringPtr Name) {
return Name != EPName;
};
S.MainJD->addGenerator(
S.ProcessSymsJD->addGenerator(
ExitOnErr(orc::EPCDynamicLibrarySearchGenerator::GetForTargetProcess(
S.ES, std::move(FilterMainEntryPoint))));

Expand All @@ -707,8 +708,9 @@ static Error loadDylibs(Session &S) {
LLVM_DEBUG(dbgs() << "Loading dylibs...\n");
for (const auto &Dylib : Dylibs) {
LLVM_DEBUG(dbgs() << " " << Dylib << "\n");
if (auto Err = S.loadAndLinkDynamicLibrary(*S.MainJD, Dylib))
return Err;
auto DL = S.getOrLoadDynamicLibrary(Dylib);
if (!DL)
return DL.takeError();
}

return Error::success();
Expand Down Expand Up @@ -963,69 +965,79 @@ Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)

ES.setErrorReporter(reportLLVMJITLinkError);

if (auto MainJDOrErr = ES.createJITDylib("main"))
MainJD = &*MainJDOrErr;
else {
Err = MainJDOrErr.takeError();
return;
}

if (!NoProcessSymbols)
ExitOnErr(loadProcessSymbols(*this));
else {
// This symbol is used in testcases.
auto &TestResultJD = ES.createBareJITDylib("<TestResultJD>");
ExitOnErr(TestResultJD.define(absoluteSymbols(
{{ES.intern("llvm_jitlink_setTestResultOverride"),
{ExecutorAddr::fromPtr(llvm_jitlink_setTestResultOverride),
JITSymbolFlags::Exported}}})));
MainJD->addToLinkOrder(TestResultJD);
}

ExitOnErr(loadDylibs(*this));

auto &TT = ES.getTargetTriple();

if (DebuggerSupport && TT.isOSBinFormatMachO())
ObjLayer.addPlugin(ExitOnErr(
GDBJITDebugInfoRegistrationPlugin::Create(this->ES, *MainJD, TT)));
if (DebuggerSupport && TT.isOSBinFormatMachO()) {
if (!ProcessSymsJD) {
Err = make_error<StringError>("MachO debugging requires process symbols",
inconvertibleErrorCode());
return;
}
ObjLayer.addPlugin(ExitOnErr(GDBJITDebugInfoRegistrationPlugin::Create(
this->ES, *ProcessSymsJD, TT)));
}

if (PerfSupport && TT.isOSBinFormatELF()) {
if (!ProcessSymsJD) {
Err = make_error<StringError>("MachO debugging requires process symbols",
inconvertibleErrorCode());
return;
}
ObjLayer.addPlugin(ExitOnErr(DebugInfoPreservationPlugin::Create()));
ObjLayer.addPlugin(ExitOnErr(PerfSupportPlugin::Create(
this->ES.getExecutorProcessControl(), *MainJD, true, true)));
this->ES.getExecutorProcessControl(), *ProcessSymsJD, true, true)));
}

// Set up the platform.
if (TT.isOSBinFormatMachO() && !OrcRuntime.empty()) {
if (auto P =
MachOPlatform::Create(ES, ObjLayer, *MainJD, OrcRuntime.c_str()))
ES.setPlatform(std::move(*P));
else {
Err = P.takeError();
return;
}
} else if (TT.isOSBinFormatELF() && !OrcRuntime.empty()) {
if (auto P =
ELFNixPlatform::Create(ES, ObjLayer, *MainJD, OrcRuntime.c_str()))
ES.setPlatform(std::move(*P));
else {
Err = P.takeError();
return;
}
} else if (TT.isOSBinFormatCOFF() && !OrcRuntime.empty()) {
auto LoadDynLibrary = [&, this](JITDylib &JD, StringRef DLLName) -> Error {
if (!DLLName.ends_with_insensitive(".dll"))
return make_error<StringError>("DLLName not ending with .dll",
inconvertibleErrorCode());
return loadAndLinkDynamicLibrary(JD, DLLName);
};
if (!OrcRuntime.empty()) {
assert(ProcessSymsJD && "ProcessSymsJD should have been set");
PlatformJD = &ES.createBareJITDylib("Platform");
PlatformJD->addToLinkOrder(*ProcessSymsJD);

if (TT.isOSBinFormatMachO()) {
if (auto P = MachOPlatform::Create(ES, ObjLayer, *PlatformJD,
OrcRuntime.c_str()))
ES.setPlatform(std::move(*P));
else {
Err = P.takeError();
return;
}
} else if (TT.isOSBinFormatELF()) {
if (auto P = ELFNixPlatform::Create(ES, ObjLayer, *PlatformJD,
OrcRuntime.c_str()))
ES.setPlatform(std::move(*P));
else {
Err = P.takeError();
return;
}
} else if (TT.isOSBinFormatCOFF()) {
auto LoadDynLibrary = [&, this](JITDylib &JD,
StringRef DLLName) -> Error {
if (!DLLName.ends_with_insensitive(".dll"))
return make_error<StringError>("DLLName not ending with .dll",
inconvertibleErrorCode());
return loadAndLinkDynamicLibrary(JD, DLLName);
};

if (auto P = COFFPlatform::Create(ES, ObjLayer, *MainJD, OrcRuntime.c_str(),
std::move(LoadDynLibrary)))
ES.setPlatform(std::move(*P));
else {
Err = P.takeError();
if (auto P = COFFPlatform::Create(ES, ObjLayer, *PlatformJD,
OrcRuntime.c_str(),
std::move(LoadDynLibrary)))
ES.setPlatform(std::move(*P));
else {
Err = P.takeError();
return;
}
} else {
Err = make_error<StringError>(
"-" + OrcRuntime.ArgStr + " specified, but format " +
Triple::getObjectFormatTypeName(TT.getObjectFormat()) +
" not supported",
inconvertibleErrorCode());
return;
}
} else if (TT.isOSBinFormatELF()) {
Expand All @@ -1037,6 +1049,24 @@ Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
ES, ExitOnErr(createJITLoaderGDBRegistrar(this->ES)), true, true));
}

if (auto MainJDOrErr = ES.createJITDylib("main"))
MainJD = &*MainJDOrErr;
else {
Err = MainJDOrErr.takeError();
return;
}

if (NoProcessSymbols) {
// This symbol is used in testcases, but we're not reflecting process
// symbols so we'll need to make it available some other way.
auto &TestResultJD = ES.createBareJITDylib("<TestResultJD>");
ExitOnErr(TestResultJD.define(absoluteSymbols(
{{ES.intern("llvm_jitlink_setTestResultOverride"),
{ExecutorAddr::fromPtr(llvm_jitlink_setTestResultOverride),
JITSymbolFlags::Exported}}})));
MainJD->addToLinkOrder(TestResultJD);
}

ObjLayer.addPlugin(std::make_unique<JITLinkSessionPlugin>(*this));

// Process any harness files.
Expand Down Expand Up @@ -1266,6 +1296,10 @@ static Error sanitizeArguments(const Triple &TT, const char *ArgV0) {
if (DebuggerSupport.getNumOccurrences() == 0 && NoExec)
DebuggerSupport = false;

if (!OrcRuntime.empty() && NoProcessSymbols)
return make_error<StringError>("-orc-runtime requires process symbols",
inconvertibleErrorCode());

// If -slab-allocate is passed, check that we're not trying to use it in
// -oop-executor or -oop-executor-connect mode.
//
Expand Down Expand Up @@ -1365,6 +1399,13 @@ static Error createJITDylibs(Session &S,
}
}

if (S.PlatformJD)
S.JDSearchOrder.push_back(
{S.PlatformJD, JITDylibLookupFlags::MatchExportedSymbolsOnly});
if (S.ProcessSymsJD)
S.JDSearchOrder.push_back(
{S.ProcessSymsJD, JITDylibLookupFlags::MatchExportedSymbolsOnly});

LLVM_DEBUG({
dbgs() << "Dylib search order is [ ";
for (auto &KV : S.JDSearchOrder)
Expand Down Expand Up @@ -1416,23 +1457,67 @@ static Error addAliases(Session &S,
const std::map<unsigned, JITDylib *> &IdxToJD) {
// Define absolute symbols.
LLVM_DEBUG(dbgs() << "Defining aliases...\n");

DenseMap<std::pair<JITDylib *, JITDylib *>, SymbolAliasMap> Reexports;
for (auto AliasItr = Aliases.begin(), AliasEnd = Aliases.end();
AliasItr != AliasEnd; ++AliasItr) {
unsigned AliasArgIdx = Aliases.getPosition(AliasItr - Aliases.begin());
auto &JD = *std::prev(IdxToJD.lower_bound(AliasArgIdx))->second;

StringRef AliasStmt = *AliasItr;
size_t EqIdx = AliasStmt.find_first_of('=');
if (EqIdx == StringRef::npos)
return make_error<StringError>("Invalid alias definition \"" + AliasStmt +
"\". Syntax: <name>=<addr>",
inconvertibleErrorCode());
StringRef Alias = AliasStmt.substr(0, EqIdx).trim();
StringRef Aliasee = AliasStmt.substr(EqIdx + 1).trim();
auto BadExpr = [&]() {
return make_error<StringError>(
"Invalid alias definition \"" + *AliasItr +
"\". Syntax: [<dst-jd>:]<alias>=[<src-jd>:]<aliasee>",
inconvertibleErrorCode());
};

auto GetJD = [&](StringRef JDName) -> Expected<JITDylib *> {
if (JDName.empty()) {
unsigned AliasArgIdx = Aliases.getPosition(AliasItr - Aliases.begin());
return std::prev(IdxToJD.lower_bound(AliasArgIdx))->second;
}

auto *JD = S.ES.getJITDylibByName(JDName);
if (!JD)
return make_error<StringError>(StringRef("In alias definition \"") +
*AliasItr + "\" no dylib named " +
JDName,
inconvertibleErrorCode());

SymbolAliasMap SAM;
SAM[S.ES.intern(Alias)] = {S.ES.intern(Aliasee), JITSymbolFlags::Exported};
if (auto Err = JD.define(symbolAliases(std::move(SAM))))
return JD;
};

{
// First split on '=' to get alias and aliasee.
StringRef AliasStmt = *AliasItr;
auto [AliasExpr, AliaseeExpr] = AliasStmt.split('=');
if (AliaseeExpr.empty())
return BadExpr();

auto [AliasJDName, Alias] = AliasExpr.split(':');
if (Alias.empty())
std::swap(AliasJDName, Alias);

auto AliasJD = GetJD(AliasJDName);
if (!AliasJD)
return AliasJD.takeError();

auto [AliaseeJDName, Aliasee] = AliaseeExpr.split(':');
if (Aliasee.empty())
std::swap(AliaseeJDName, Aliasee);

if (AliaseeJDName.empty() && !AliasJDName.empty())
AliaseeJDName = AliasJDName;
auto AliaseeJD = GetJD(AliaseeJDName);
if (!AliaseeJD)
return AliaseeJD.takeError();

Reexports[{*AliasJD, *AliaseeJD}][S.ES.intern(Alias)] = {
S.ES.intern(Aliasee), JITSymbolFlags::Exported};
}
}

for (auto &[JDs, AliasMap] : Reexports) {
auto [DstJD, SrcJD] = JDs;
if (auto Err = DstJD->define(reexports(*SrcJD, std::move(AliasMap))))
return Err;
}

Expand Down Expand Up @@ -1766,6 +1851,14 @@ static Error addLibraries(Session &S,
inconvertibleErrorCode());
}

// Add platform and process symbols if available.
for (auto &[Idx, JD] : IdxToJD) {
if (S.PlatformJD)
JD->addToLinkOrder(*S.PlatformJD);
if (S.ProcessSymsJD)
JD->addToLinkOrder(*S.ProcessSymsJD);
}

return Error::success();
}

Expand Down
2 changes: 2 additions & 0 deletions llvm/tools/llvm-jitlink/llvm-jitlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ struct Session {

orc::ExecutionSession ES;
orc::JITDylib *MainJD = nullptr;
orc::JITDylib *ProcessSymsJD = nullptr;
orc::JITDylib *PlatformJD = nullptr;
orc::ObjectLinkingLayer ObjLayer;
orc::JITDylibSearchOrder JDSearchOrder;
SubtargetFeatures Features;
Expand Down
10 changes: 0 additions & 10 deletions llvm/unittests/tools/llvm-exegesis/X86/SnippetFileTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,16 +209,6 @@ TEST_F(X86SnippetFileTest, MemoryMappingNoDefinition) {
consumeError(std::move(Error));
}

TEST_F(X86SnippetFileTest, SnippetAddress) {
auto Snippets = TestCommon(R"(
# LLVM-EXEGESIS-SNIPPET-ADDRESS 0x10000
)");
ASSERT_TRUE(static_cast<bool>(Snippets));
EXPECT_THAT(*Snippets, SizeIs(1));
const auto &Snippet = (*Snippets)[0];
EXPECT_EQ(Snippet.Key.SnippetAddress, 0x10000);
}

} // namespace
} // namespace exegesis
} // namespace llvm