xref: /aosp_15_r20/external/swiftshader/third_party/marl/src/osfiber_asm.h (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1 // Copyright 2019 The Marl Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Minimal assembly implementations of fiber context switching for Unix-based
16 // platforms.
17 //
18 // Note: Unlike makecontext, swapcontext or the Windows fiber APIs, these
19 // assembly implementations *do not* save or restore signal masks,
20 // floating-point control or status registers, FS and GS segment registers,
21 // thread-local storage state nor any SIMD registers. This should not be a
22 // problem as the marl scheduler requires fibers to be executed on the same
23 // thread throughout their lifetime.
24 
25 #if defined(__x86_64__)
26 #include "osfiber_asm_x64.h"
27 #elif defined(__i386__)
28 #include "osfiber_asm_x86.h"
29 #elif defined(__aarch64__)
30 #include "osfiber_asm_aarch64.h"
31 #elif defined(__arm__)
32 #include "osfiber_asm_arm.h"
33 #elif defined(__powerpc64__)
34 #include "osfiber_asm_ppc64.h"
35 #elif defined(__mips__) && _MIPS_SIM == _ABI64
36 #include "osfiber_asm_mips64.h"
37 #elif defined(__riscv) && __riscv_xlen == 64
38 #include "osfiber_asm_rv64.h"
39 #elif defined(__loongarch__) && _LOONGARCH_SIM == _ABILP64
40 #include "osfiber_asm_loongarch64.h"
41 #elif defined(__EMSCRIPTEN__)
42 #include "osfiber_emscripten.h"
43 #else
44 #error "Unsupported target"
45 #endif
46 
47 #include "marl/export.h"
48 #include "marl/memory.h"
49 
50 #include <functional>
51 #include <memory>
52 
53 extern "C" {
54 
55 #if defined(__EMSCRIPTEN__)
56 MARL_EXPORT
57 void marl_main_fiber_init(marl_fiber_context* ctx);
58 #else
59 MARL_EXPORT
60 inline void marl_main_fiber_init(marl_fiber_context*) {}
61 #endif
62 MARL_EXPORT
63 extern void marl_fiber_set_target(marl_fiber_context*,
64                                   void* stack,
65                                   uint32_t stack_size,
66                                   void (*target)(void*),
67                                   void* arg);
68 MARL_EXPORT
69 extern void marl_fiber_swap(marl_fiber_context* from,
70                             const marl_fiber_context* to);
71 
72 }  // extern "C"
73 
74 namespace marl {
75 
76 class OSFiber {
77  public:
78   inline OSFiber(Allocator*);
79   inline ~OSFiber();
80 
81   // createFiberFromCurrentThread() returns a fiber created from the current
82   // thread.
83   MARL_NO_EXPORT static inline Allocator::unique_ptr<OSFiber>
84   createFiberFromCurrentThread(Allocator* allocator);
85 
86   // createFiber() returns a new fiber with the given stack size that will
87   // call func when switched to. func() must end by switching back to another
88   // fiber, and must not return.
89   MARL_NO_EXPORT static inline Allocator::unique_ptr<OSFiber> createFiber(
90       Allocator* allocator,
91       size_t stackSize,
92       const std::function<void()>& func);
93 
94   // switchTo() immediately switches execution to the given fiber.
95   // switchTo() must be called on the currently executing fiber.
96   MARL_NO_EXPORT inline void switchTo(OSFiber*);
97 
98  private:
99   MARL_NO_EXPORT
100   static inline void run(OSFiber* self);
101 
102   Allocator* allocator;
103   marl_fiber_context context;
104   std::function<void()> target;
105   Allocation stack;
106 };
107 
OSFiber(Allocator * allocator)108 OSFiber::OSFiber(Allocator* allocator) : allocator(allocator) {}
109 
~OSFiber()110 OSFiber::~OSFiber() {
111   if (stack.ptr != nullptr) {
112     allocator->free(stack);
113   }
114 }
115 
createFiberFromCurrentThread(Allocator * allocator)116 Allocator::unique_ptr<OSFiber> OSFiber::createFiberFromCurrentThread(
117     Allocator* allocator) {
118   auto out = allocator->make_unique<OSFiber>(allocator);
119   out->context = {};
120   marl_main_fiber_init(&out->context);
121   return out;
122 }
123 
createFiber(Allocator * allocator,size_t stackSize,const std::function<void ()> & func)124 Allocator::unique_ptr<OSFiber> OSFiber::createFiber(
125     Allocator* allocator,
126     size_t stackSize,
127     const std::function<void()>& func) {
128   Allocation::Request request;
129   request.size = stackSize;
130   request.alignment = 16;
131   request.usage = Allocation::Usage::Stack;
132 #if MARL_USE_FIBER_STACK_GUARDS
133   request.useGuards = true;
134 #endif
135 
136   auto out = allocator->make_unique<OSFiber>(allocator);
137   out->context = {};
138   out->target = func;
139   out->stack = allocator->allocate(request);
140   marl_fiber_set_target(
141       &out->context, out->stack.ptr, static_cast<uint32_t>(stackSize),
142       reinterpret_cast<void (*)(void*)>(&OSFiber::run), out.get());
143   return out;
144 }
145 
run(OSFiber * self)146 void OSFiber::run(OSFiber* self) {
147   self->target();
148 }
149 
switchTo(OSFiber * fiber)150 void OSFiber::switchTo(OSFiber* fiber) {
151   marl_fiber_swap(&context, &fiber->context);
152 }
153 
154 }  // namespace marl
155