Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

can we use alloca() or variable length array extentions in c++20 coroutines?

The GCC C++ compiler (any many other C++ compilers as well) provide nonstandard extentions such as

  • alloca() for stack based allocation
  • variable length arrays, as they are part of the C standard

Can these be used inside of C++20 coroutines from a fundamental point of view? Is it possible at all? And if yes how is this implemented?

As far as I understood is that the C++20 coroutines generally create the stack-frame for the coroutine on the first call (i.e. when the promise object is created) and hence need to know the size of the coroutines stack-frame.

However this does not play nicely with alloca or other run-time dynamic stack allocation.

So is it possible and, if yes, how it is implemented? Or what are the implications?

like image 985
Andreas H. Avatar asked May 17 '21 19:05

Andreas H.


People also ask

Can I use variable as array size in C?

int size means that size is a variable and C does not allow variable size arrays.

How do you use variable length arrays?

A variable length array can be used in a typedef statement. The typedef name will have only block scope. The length of the array is fixed when the typedef name is defined, not each time it is used. A function parameter can be a variable length array.

Are variable length arrays bad C?

Safe and useful variable length arraysIt's safe because there's no arbitrary stack allocation. Pointers to arrays are a rare sight in C code, whether variable length or not.

Does clang support variable length arrays?

Variable-length arrays GCC and C99 allow an array's size to be determined at run time. This extension is not permitted in standard C++. However, Clang supports such variable length arrays for compatibility with GNU C and C99 programs.


1 Answers

Unfortunately, alloca is not compatible with C++20 coroutines in GCC. And the worst thing is that the compiler does not warn about it.

This code example demonstrates the incompatibility:

#include <coroutine>
#include <iostream>

struct ReturnObject {
  struct promise_type {
    unsigned * value_ = nullptr;

    void return_void() {}
    ReturnObject get_return_object() {
      return {
        .h_ = std::coroutine_handle<promise_type>::from_promise(*this)
      };
    }
    std::suspend_never initial_suspend() { return {}; }
    std::suspend_never final_suspend() noexcept { return {}; }
    void unhandled_exception() {}
  };

  std::coroutine_handle<promise_type> h_;
  operator auto() const { return h_; }
};

template<typename PromiseType>
struct GetPromise {
  PromiseType *p_;
  bool await_ready() { return false; }
  bool await_suspend(std::coroutine_handle<PromiseType> h) {
    p_ = &h.promise();
    return false;
  }
  PromiseType *await_resume() { return p_; }
};

ReturnObject counter()
{
  auto pp = co_await GetPromise<ReturnObject::promise_type>{};

  //unsigned a[1]; auto & i = a[0]; //this version works fine
  auto & i = *new (alloca(sizeof(unsigned))) unsigned(0); //and this not
  for (;; ++i) {
    pp->value_ = &i;
    co_await std::suspend_always{};
  }
}

int main()
{
  std::coroutine_handle<ReturnObject::promise_type> h = counter();
  auto &promise = h.promise();
  for (int i = 0; i < 5; ++i) {
    std::cout << *promise.value_ << std::endl;
    h();
  }
  h.destroy();
}

https://gcc.godbolt.org/z/8zG446Esx

It should print 0 1 2 3 4, but does not precisely due to the usage of alloca

like image 157
Fedor Avatar answered Sep 26 '22 04:09

Fedor