| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,237 @@ | ||
| // This is reduced test case from https://github.com/llvm/llvm-project/issues/59723. | ||
| // This is not a minimal reproducer intentionally to check the compiler's ability. | ||
| // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fcxx-exceptions\ | ||
| // RUN: -fexceptions -O2 -emit-llvm %s -o - | FileCheck %s | ||
|
|
||
| #include "Inputs/coroutine.h" | ||
|
|
||
| // executor and operation base | ||
|
|
||
| class bug_any_executor; | ||
|
|
||
| struct bug_async_op_base | ||
| { | ||
| void invoke(); | ||
|
|
||
| protected: | ||
|
|
||
| ~bug_async_op_base() = default; | ||
| }; | ||
|
|
||
| class bug_any_executor | ||
| { | ||
| using op_type = bug_async_op_base; | ||
|
|
||
| public: | ||
|
|
||
| virtual ~bug_any_executor() = default; | ||
|
|
||
| // removing noexcept enables clang to find that the pointer has escaped | ||
| virtual void post(op_type& op) noexcept = 0; | ||
|
|
||
| virtual void wait() noexcept = 0; | ||
| }; | ||
|
|
||
| class bug_thread_executor : public bug_any_executor | ||
| { | ||
|
|
||
| public: | ||
|
|
||
| void start() | ||
| { | ||
|
|
||
| } | ||
|
|
||
| ~bug_thread_executor() | ||
| { | ||
| } | ||
|
|
||
| // although this implementation is not realy noexcept due to allocation but I have a real one that is and required to be noexcept | ||
| virtual void post(bug_async_op_base& op) noexcept override; | ||
|
|
||
| virtual void wait() noexcept override | ||
| { | ||
|
|
||
| } | ||
| }; | ||
|
|
||
| // task and promise | ||
|
|
||
| struct bug_final_suspend_notification | ||
| { | ||
| virtual std::coroutine_handle<> get_waiter() = 0; | ||
| }; | ||
|
|
||
| class bug_task; | ||
|
|
||
| class bug_task_promise | ||
| { | ||
| friend bug_task; | ||
| public: | ||
|
|
||
| bug_task get_return_object() noexcept; | ||
|
|
||
| constexpr std::suspend_always initial_suspend() noexcept { return {}; } | ||
|
|
||
| std::suspend_always final_suspend() noexcept | ||
| { | ||
| return {}; | ||
| } | ||
|
|
||
| void unhandled_exception() noexcept; | ||
|
|
||
| constexpr void return_void() const noexcept {} | ||
|
|
||
| void get_result() const | ||
| { | ||
|
|
||
| } | ||
| }; | ||
|
|
||
| template <class T, class U> | ||
| T exchange(T &&t, U &&u) { | ||
| T ret = t; | ||
| t = u; | ||
| return ret; | ||
| } | ||
|
|
||
| class bug_task | ||
| { | ||
| friend bug_task_promise; | ||
| using handle = std::coroutine_handle<>; | ||
| using promise_t = bug_task_promise; | ||
|
|
||
| bug_task(handle coro, promise_t* p) noexcept : this_coro{ coro }, this_promise{ p } | ||
| { | ||
|
|
||
| } | ||
|
|
||
| public: | ||
| using promise_type = bug_task_promise; | ||
|
|
||
| bug_task(bug_task&& other) noexcept | ||
| : this_coro{ exchange(other.this_coro, nullptr) }, this_promise{ exchange(other.this_promise, nullptr) } { | ||
|
|
||
| } | ||
|
|
||
| ~bug_task() | ||
| { | ||
| if (this_coro) | ||
| this_coro.destroy(); | ||
| } | ||
|
|
||
| constexpr bool await_ready() const noexcept | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| handle await_suspend(handle waiter) noexcept | ||
| { | ||
| return this_coro; | ||
| } | ||
|
|
||
| void await_resume() | ||
| { | ||
| return this_promise->get_result(); | ||
| } | ||
|
|
||
| handle this_coro; | ||
| promise_t* this_promise; | ||
| }; | ||
|
|
||
| bug_task bug_task_promise::get_return_object() noexcept | ||
| { | ||
| return { std::coroutine_handle<bug_task_promise>::from_promise(*this), this }; | ||
| } | ||
|
|
||
| // spawn operation and spawner | ||
|
|
||
| template<class Handler> | ||
| class bug_spawn_op final : public bug_async_op_base, bug_final_suspend_notification | ||
| { | ||
| Handler handler; | ||
| bug_task task_; | ||
|
|
||
| public: | ||
|
|
||
| bug_spawn_op(Handler handler, bug_task&& t) | ||
| : handler { handler }, task_{ static_cast<bug_task&&>(t) } {} | ||
|
|
||
| virtual std::coroutine_handle<> get_waiter() override | ||
| { | ||
| handler(); | ||
| return std::noop_coroutine(); | ||
| } | ||
| }; | ||
|
|
||
| class bug_spawner; | ||
|
|
||
| struct bug_spawner_awaiter | ||
| { | ||
| bug_spawner& s; | ||
| std::coroutine_handle<> waiter; | ||
|
|
||
| bug_spawner_awaiter(bug_spawner& s) : s{ s } {} | ||
|
|
||
| bool await_ready() const noexcept; | ||
|
|
||
| void await_suspend(std::coroutine_handle<> coro); | ||
|
|
||
| void await_resume() {} | ||
| }; | ||
|
|
||
| class bug_spawner | ||
| { | ||
| friend bug_spawner_awaiter; | ||
|
|
||
| struct final_handler_t | ||
| { | ||
| bug_spawner& s; | ||
|
|
||
| void operator()() | ||
| { | ||
| s.awaiter_->waiter.resume(); | ||
| } | ||
| }; | ||
|
|
||
| public: | ||
|
|
||
| bug_spawner(bug_any_executor& ex) : ex_{ ex } {} | ||
|
|
||
| void spawn(bug_task&& t) { | ||
| using op_t = bug_spawn_op<final_handler_t>; | ||
| // move task into ptr | ||
| op_t* ptr = new op_t(final_handler_t{ *this }, static_cast<bug_task&&>(t)); | ||
| ++count_; | ||
| ex_.post(*ptr); // ptr escapes here thus task escapes but clang can't deduce that unless post() is not noexcept | ||
| } | ||
|
|
||
| bug_spawner_awaiter wait() noexcept { return { *this }; } | ||
|
|
||
| private: | ||
| bug_any_executor& ex_; // if bug_thread_executor& is used instead enables clang to detect the escape of the promise | ||
| bug_spawner_awaiter* awaiter_ = nullptr; | ||
| unsigned count_ = 0; | ||
| }; | ||
|
|
||
| // test case | ||
|
|
||
| bug_task bug_spawned_task(int id, int inc) | ||
| { | ||
| co_return; | ||
| } | ||
|
|
||
| struct A { | ||
| A(); | ||
| }; | ||
|
|
||
| void throwing_fn(bug_spawner& s) { | ||
| s.spawn(bug_spawned_task(1, 2)); | ||
| throw A{}; | ||
| } | ||
|
|
||
| // Check that the coroutine frame of bug_spawned_task are allocated from operator new. | ||
| // CHECK: define{{.*}}@_Z11throwing_fnR11bug_spawner | ||
| // CHECK-NOT: alloc | ||
| // CHECK: %[[CALL:.+]] = {{.*}}@_Znwm(i64{{.*}} 24) | ||
| // CHECK: store ptr @_Z16bug_spawned_taskii.resume, ptr %[[CALL]] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,8 @@ | ||
| /// Tests -mno-gather and -mno-scatter | ||
| // RUN: %clang -target x86_64-unknown-linux-gnu -c -mno-gather -### %s 2>&1 | FileCheck --check-prefix=NOGATHER %s | ||
| // RUN: %clang_cl --target=x86_64-windows -c /Qgather- -### -- %s 2>&1 | FileCheck --check-prefix=NOGATHER %s | ||
| // NOGATHER: "-target-feature" "+prefer-no-gather" | ||
|
|
||
| // RUN: %clang -target x86_64-unknown-linux-gnu -c -mno-scatter -### %s 2>&1 | FileCheck --check-prefix=NOSCATTER %s | ||
| // RUN: %clang_cl --target=x86_64-windows -c /Qscatter- -### -- %s 2>&1 | FileCheck --check-prefix=NOSCATTER %s | ||
| // NOSCATTER: "-target-feature" "+prefer-no-scatter" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // RUN: %clang_cc1 -triple x86_64-linux-gnu -ast-dump -ast-dump-filter=b_64605 %s | FileCheck %s | ||
|
|
||
| // https://github.com/llvm/llvm-project/issues/64605 | ||
|
|
||
| #pragma STDC FENV_ACCESS ON | ||
| template <typename> | ||
| int b_64605() { | ||
| int x; | ||
| if ((float)0xFFFFFFFF != (float)0x100000000) { | ||
| x = 1; | ||
| } | ||
| return x; | ||
| } | ||
| int f() { return b_64605<void>(); } | ||
|
|
||
| // CHECK: ImplicitCastExpr {{.*}} 'float' <IntegralToFloating> RoundingMath=1 AllowFEnvAccess=1 | ||
| // CHECK-NEXT: IntegerLiteral {{.*}} 4294967295 | ||
|
|
||
| // CHECK: FunctionDecl {{.*}} b_64605 'int ()' implicit_instantiation | ||
| // CHECK-NEXT: TemplateArgument type 'void' | ||
|
|
||
| // CHECK: ImplicitCastExpr {{.*}} 'float' <IntegralToFloating> RoundingMath=1 AllowFEnvAccess=1 | ||
| // CHECK-NEXT: IntegerLiteral {{.*}} 4294967295 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| # RUN: llc -o - %s -mtriple=x86_64-pc-windows-msvc -run-pass=machinelicm | FileCheck %s | ||
| # | ||
| # This test checks that MachineLICM doesn't hoist loads out of funclets. | ||
| # Manually modified from the IR of the following C++ function by running | ||
| # llc -stop-after=machine-cp. | ||
| # | ||
| # void may_throw(); | ||
| # void use(int); | ||
| # | ||
| # void test(int n, int arg) | ||
| # { | ||
| # for (int i = 0 ; i < n ; i++) | ||
| # try { | ||
| # may_throw(); | ||
| # } | ||
| # catch (...) { | ||
| # // Two uses to get 'arg' allocated to a register | ||
| # use(arg); | ||
| # use(arg); | ||
| # } | ||
| # } | ||
|
|
||
| --- | | ||
| target triple = "x86_64-pc-windows-msvc" | ||
|
|
||
| define void @test(i32 %n, i32 %arg) personality ptr @__CxxFrameHandler3 { | ||
| entry: | ||
| %cmp3 = icmp sgt i32 %n, 0 | ||
| br i1 %cmp3, label %for.body.preheader, label %for.cond.cleanup | ||
|
|
||
| for.body.preheader: ; preds = %entry | ||
| br label %for.body | ||
|
|
||
| for.cond.cleanup: ; preds = %for.inc, %entry | ||
| ret void | ||
|
|
||
| for.body: ; preds = %for.body.preheader, %for.inc | ||
| %lsr.iv = phi i32 [ %n, %for.body.preheader ], [ %lsr.iv.next, %for.inc ] | ||
| invoke void @may_throw() | ||
| to label %for.inc unwind label %catch.dispatch | ||
|
|
||
| catch.dispatch: ; preds = %for.body | ||
| %0 = catchswitch within none [label %catch] unwind to caller | ||
|
|
||
| catch: ; preds = %catch.dispatch | ||
| %1 = catchpad within %0 [ptr null, i32 64, ptr null] | ||
| call void @use(i32 %arg) [ "funclet"(token %1) ] | ||
| call void @use(i32 %arg) [ "funclet"(token %1) ] | ||
| catchret from %1 to label %for.inc | ||
|
|
||
| for.inc: ; preds = %catch, %for.body | ||
| %lsr.iv.next = add i32 %lsr.iv, -1 | ||
| %exitcond.not = icmp eq i32 %lsr.iv.next, 0 | ||
| br i1 %exitcond.not, label %for.cond.cleanup, label %for.body | ||
| } | ||
|
|
||
| declare i32 @__CxxFrameHandler3(...) | ||
|
|
||
| declare void @may_throw() | ||
|
|
||
| declare void @use(i32) | ||
|
|
||
| ... | ||
| --- | ||
| name: test | ||
| alignment: 16 | ||
| tracksRegLiveness: true | ||
| hasEHCatchret: true | ||
| hasEHScopes: true | ||
| hasEHFunclets: true | ||
| debugInstrRef: true | ||
| tracksDebugUserValues: true | ||
| liveins: | ||
| - { reg: '$ecx' } | ||
| - { reg: '$edx' } | ||
| frameInfo: | ||
| maxAlignment: 8 | ||
| hasCalls: true | ||
| hasOpaqueSPAdjustment: true | ||
| stack: | ||
| - { id: 0, type: spill-slot, size: 4, alignment: 4 } | ||
| - { id: 1, type: spill-slot, size: 4, alignment: 4 } | ||
| machineFunctionInfo: {} | ||
| body: | | ||
| bb.0.entry: | ||
| successors: %bb.1, %bb.2 | ||
| liveins: $ecx, $edx | ||
| MOV32mr %stack.1, 1, $noreg, 0, $noreg, $edx :: (store (s32) into %stack.1) | ||
| TEST32rr renamable $ecx, renamable $ecx, implicit-def $eflags | ||
| JCC_1 %bb.2, 14, implicit killed $eflags | ||
| bb.1: | ||
| liveins: $ecx | ||
| JMP_1 %bb.3 | ||
| bb.2.for.cond.cleanup: | ||
| RET 0 | ||
| bb.3.for.body: | ||
| successors: %bb.5, %bb.4 | ||
| liveins: $ecx | ||
| EH_LABEL <mcsymbol .Leh1> | ||
| MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $ecx :: (store (s32) into %stack.0) | ||
| ADJCALLSTACKDOWN64 32, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp | ||
| CALL64pcrel32 @may_throw, csr_win64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp | ||
| ADJCALLSTACKUP64 32, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp | ||
| EH_LABEL <mcsymbol .Leh2> | ||
| JMP_1 %bb.5 | ||
| bb.4.catch (landing-pad, ehfunclet-entry): | ||
| successors: %bb.5 | ||
| ADJCALLSTACKDOWN64 32, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp | ||
| renamable $esi = MOV32rm %stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %stack.1) | ||
| $ecx = COPY renamable $esi | ||
| CALL64pcrel32 @use, csr_win64, implicit $rsp, implicit $ssp, implicit $ecx, implicit-def $rsp, implicit-def $ssp | ||
| ADJCALLSTACKUP64 32, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp | ||
| ADJCALLSTACKDOWN64 32, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp | ||
| $ecx = COPY killed renamable $esi | ||
| CALL64pcrel32 @use, csr_win64, implicit $rsp, implicit $ssp, implicit $ecx, implicit-def $rsp, implicit-def $ssp | ||
| ADJCALLSTACKUP64 32, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp | ||
| CATCHRET %bb.5, %bb.0 | ||
| bb.5.for.inc: | ||
| successors: %bb.2, %bb.3 | ||
| renamable $ecx = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0) | ||
| renamable $ecx = DEC32r killed renamable $ecx, implicit-def $eflags | ||
| JCC_1 %bb.2, 4, implicit killed $eflags | ||
| JMP_1 %bb.3 | ||
| ... | ||
| # | ||
| # CHECK: bb.4.catch | ||
| # CHECK: ADJCALLSTACKDOWN64 | ||
| # CHECK-NEXT: renamable [[REG:\$[a-z0-9]+]] = MOV32rm %stack.1 | ||
| # CHECK-NEXT: $ecx = COPY renamable [[REG]] | ||
| # CHECK-NEXT: CALL64pcrel32 @use |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| // RUN: %libomptarget-compile-run-and-check-generic | ||
| // REQUIRES: ompt | ||
| // UNSUPPORTED: aarch64-unknown-linux-gnu | ||
| // UNSUPPORTED: aarch64-unknown-linux-gnu-LTO | ||
| // UNSUPPORTED: x86_64-pc-linux-gnu | ||
| // UNSUPPORTED: x86_64-pc-linux-gnu-oldDriver | ||
| // UNSUPPORTED: x86_64-pc-linux-gnu-LTO | ||
|
|
||
| /* | ||
| * Example OpenMP program that registers EMI callbacks. | ||
| * Explicitly testing for an initialized device num and | ||
| * #pragma omp target [data enter / data exit / update] | ||
| * The latter with the addition of a nowait clause. | ||
| */ | ||
|
|
||
| #include <omp.h> | ||
| #include <stdio.h> | ||
|
|
||
| #include "callbacks.h" | ||
| #include "register_emi.h" | ||
|
|
||
| #define N 100000 | ||
|
|
||
| #pragma omp declare target | ||
| int c[N]; | ||
| #pragma omp end declare target | ||
|
|
||
| int main() { | ||
| int a[N]; | ||
| int b[N]; | ||
|
|
||
| int i; | ||
|
|
||
| for (i = 0; i < N; i++) | ||
| a[i] = 0; | ||
|
|
||
| for (i = 0; i < N; i++) | ||
| b[i] = i; | ||
|
|
||
| for (i = 0; i < N; i++) | ||
| c[i] = 0; | ||
|
|
||
| #pragma omp target enter data map(to : a) | ||
| #pragma omp target parallel for | ||
| { | ||
| for (int j = 0; j < N; j++) | ||
| a[j] = b[j]; | ||
| } | ||
| #pragma omp target exit data map(from : a) | ||
|
|
||
| #pragma omp target parallel for map(alloc : c) | ||
| { | ||
| for (int j = 0; j < N; j++) | ||
| c[j] = 2 * j + 1; | ||
| } | ||
| #pragma omp target update from(c) nowait | ||
| #pragma omp barrier | ||
|
|
||
| int rc = 0; | ||
| for (i = 0; i < N; i++) { | ||
| if (a[i] != i) { | ||
| rc++; | ||
| printf("Wrong value: a[%d]=%d\n", i, a[i]); | ||
| } | ||
| } | ||
|
|
||
| for (i = 0; i < N; i++) { | ||
| if (c[i] != 2 * i + 1) { | ||
| rc++; | ||
| printf("Wrong value: c[%d]=%d\n", i, c[i]); | ||
| } | ||
| } | ||
|
|
||
| if (!rc) | ||
| printf("Success\n"); | ||
|
|
||
| return rc; | ||
| } | ||
|
|
||
| /// CHECK-NOT: Callback Target EMI: | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback Init: | ||
| /// CHECK: Callback Load: | ||
| /// CHECK: Callback Target EMI: kind=2 endpoint=1 | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback DataOp EMI: endpoint=1 optype=1 | ||
| /// CHECK: Callback DataOp EMI: endpoint=2 optype=1 | ||
| /// CHECK-NOT: dest=(nil) | ||
| /// CHECK: Callback DataOp EMI: endpoint=1 optype=2 | ||
| /// CHECK: Callback DataOp EMI: endpoint=2 optype=2 | ||
| /// CHECK: Callback Target EMI: kind=2 endpoint=2 | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback Target EMI: kind=1 endpoint=1 | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback DataOp EMI: endpoint=1 optype=1 | ||
| /// CHECK: Callback DataOp EMI: endpoint=2 optype=1 | ||
| /// CHECK-NOT: dest=(nil) | ||
| /// CHECK: Callback DataOp EMI: endpoint=1 optype=2 | ||
| /// CHECK: Callback DataOp EMI: endpoint=2 optype=2 | ||
| /// CHECK: Callback Submit EMI: endpoint=1 req_num_teams=1 | ||
| /// CHECK: Callback Submit EMI: endpoint=2 req_num_teams=1 | ||
| /// CHECK: Callback DataOp EMI: endpoint=1 optype=3 | ||
| /// CHECK: Callback DataOp EMI: endpoint=2 optype=3 | ||
| /// CHECK: Callback DataOp EMI: endpoint=1 optype=4 | ||
| /// CHECK: Callback DataOp EMI: endpoint=2 optype=4 | ||
| /// CHECK: Callback Target EMI: kind=1 endpoint=2 | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback Target EMI: kind=3 endpoint=1 | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback DataOp EMI: endpoint=1 optype=3 | ||
| /// CHECK: Callback DataOp EMI: endpoint=2 optype=3 | ||
| /// CHECK: Callback DataOp EMI: endpoint=1 optype=4 | ||
| /// CHECK: Callback DataOp EMI: endpoint=2 optype=4 | ||
| /// CHECK: Callback Target EMI: kind=3 endpoint=2 | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback Target EMI: kind=1 endpoint=1 | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback Submit EMI: endpoint=1 req_num_teams=1 | ||
| /// CHECK: Callback Submit EMI: endpoint=2 req_num_teams=1 | ||
| /// CHECK: Callback Target EMI: kind=1 endpoint=2 | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback Target EMI: kind=4 endpoint=1 | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback DataOp EMI: endpoint=1 optype=3 | ||
| /// CHECK: Callback DataOp EMI: endpoint=2 optype=3 | ||
| /// CHECK: Callback Target EMI: kind=4 endpoint=2 | ||
| /// CHECK-NOT: device_num=-1 | ||
| /// CHECK: Callback Fini: |