Commit ad9f510a by Ben Clayton

Yarn: Implement fiber context switching for Windows, macOS, Linux and Android.

Windows uses OSFiber_windows.hpp. macOS and Linux uses OSFiber_asm_* or OSFiber_ucontext.hpp Android has no support for makecontext / swapcontext, and so relies on the OSFiber_asm_* implementations. Unlike makecontext, swapcontext or the Windows fiber APIs, these assembly implementations *do not* save or restore signal masks, floating-point control or status registers, FS and GS segment registers, thread-local storage state nor any SIMD registers. This should not be a problem as the yarn scheduler requires fibers to be executed on a single thread. Bug: b/139010488 Change-Id: I5f890ffe4f069361a7eeae64567a77be3a4ba008 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/34771Tested-by: 's avatarBen Clayton <bclayton@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
parent 19e13a38
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(_WIN32)
#include "OSFiber_windows.hpp"
#elif defined(YARN_FIBERS_USE_UCONTEXT)
#include "OSFiber_ucontext.hpp"
#else
#include "OSFiber_asm.hpp"
#endif
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Minimal assembly implementations of fiber context switching for Unix-based
// platforms.
//
// Note: Unlike makecontext, swapcontext or the Windows fiber APIs, these
// assembly implementations *do not* save or restore signal masks,
// floating-point control or status registers, FS and GS segment registers,
// thread-local storage state nor any SIMD registers. This should not be a
// problem as the yarn scheduler requires fibers to be executed on a single
// thread.
#if defined(__x86_64__)
#include "OSFiber_asm_x64.h"
#elif defined(__i386__)
#include "OSFiber_asm_x86.h"
#elif defined(__aarch64__)
#include "OSFiber_asm_aarch64.h"
#elif defined(__arm__)
#include "OSFiber_asm_arm.h"
#else
#error "Unsupported target"
#endif
#include <functional>
#include <memory>
extern "C"
{
extern void yarn_fiber_set_target(yarn_fiber_context*, void* stack, uint32_t stack_size, void(*target)(void*), void* arg);
extern void yarn_fiber_swap(yarn_fiber_context* from, const yarn_fiber_context* to);
} // extern "C"
namespace yarn {
class OSFiber
{
public:
// createFiberFromCurrentThread() returns a fiber created from the current
// thread.
static inline OSFiber* createFiberFromCurrentThread();
// createFiber() returns a new fiber with the given stack size that will
// call func when switched to. func() must end by switching back to another
// fiber, and must not return.
static inline OSFiber* createFiber(size_t stackSize, const std::function<void()>& func);
// switchTo() immediately switches execution to the given fiber.
// switchTo() must be called on the currently executing fiber.
inline void switchTo(OSFiber*);
private:
static inline void run(OSFiber* self);
yarn_fiber_context context;
std::function<void()> target;
std::unique_ptr<uint8_t[]> stack;
};
OSFiber* OSFiber::createFiberFromCurrentThread()
{
auto out = new OSFiber();
out->context = {};
return out;
}
OSFiber* OSFiber::createFiber(size_t stackSize, const std::function<void()>& func)
{
auto out = new OSFiber();
out->context = {};
out->target = func;
out->stack = std::unique_ptr<uint8_t[]>(new uint8_t[stackSize]);
yarn_fiber_set_target(&out->context, out->stack.get(), stackSize, reinterpret_cast<void (*)(void*)>(&OSFiber::run), out);
return out;
}
void OSFiber::run(OSFiber* self)
{
std::function<void()> func;
std::swap(func, self->target);
func();
}
void OSFiber::switchTo(OSFiber* fiber)
{
yarn_fiber_swap(&context, &fiber->context);
}
} // namespace yarn
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(__aarch64__)
#include "OSFiber_asm_aarch64.h"
void yarn_fiber_trampoline(void(*target)(void*), void* arg)
{
target(arg);
}
void yarn_fiber_set_target(struct yarn_fiber_context* ctx, void* stack, uint32_t stack_size, void(*target)(void*), void* arg)
{
uintptr_t* stack_top = (uintptr_t*)((uint8_t*)(stack) + stack_size);
ctx->LR = (uintptr_t)&yarn_fiber_trampoline;
ctx->r0 = (uintptr_t)target;
ctx->r1 = (uintptr_t)arg;
ctx->SP = ((uintptr_t)stack_top) & ~(uintptr_t)15;
}
#endif // defined(__aarch64__)
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#define YARN_REG_r0 0x00
#define YARN_REG_r1 0x08
#define YARN_REG_r16 0x10
#define YARN_REG_r17 0x18
#define YARN_REG_r18 0x20
#define YARN_REG_r19 0x28
#define YARN_REG_r20 0x30
#define YARN_REG_r21 0x38
#define YARN_REG_r22 0x40
#define YARN_REG_r23 0x48
#define YARN_REG_r24 0x50
#define YARN_REG_r25 0x58
#define YARN_REG_r26 0x60
#define YARN_REG_r27 0x68
#define YARN_REG_r28 0x70
#define YARN_REG_v8 0x78
#define YARN_REG_v9 0x80
#define YARN_REG_v10 0x88
#define YARN_REG_v11 0x90
#define YARN_REG_v12 0x98
#define YARN_REG_v13 0xa0
#define YARN_REG_v14 0xa8
#define YARN_REG_v15 0xb0
#define YARN_REG_SP 0xb8
#define YARN_REG_LR 0xc0
#if defined(__APPLE__)
#define YARN_ASM_SYMBOL(x) _##x
#else
#define YARN_ASM_SYMBOL(x) x
#endif
#ifndef BUILD_ASM
#include <stdint.h>
// Procedure Call Standard for the ARM 64-bit Architecture
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
struct yarn_fiber_context
{
// parameter registers
uintptr_t r0;
uintptr_t r1;
// special purpose registers
uintptr_t r16;
uintptr_t r17;
uintptr_t r18; // platform specific (maybe inter-procedural state)
// callee-saved registers
uintptr_t r19;
uintptr_t r20;
uintptr_t r21;
uintptr_t r22;
uintptr_t r23;
uintptr_t r24;
uintptr_t r25;
uintptr_t r26;
uintptr_t r27;
uintptr_t r28;
uintptr_t v8;
uintptr_t v9;
uintptr_t v10;
uintptr_t v11;
uintptr_t v12;
uintptr_t v13;
uintptr_t v14;
uintptr_t v15;
uintptr_t SP; // stack pointer
uintptr_t LR; // link register (R30)
};
#ifdef __cplusplus
#include <cstddef>
static_assert(offsetof(yarn_fiber_context, r0) == YARN_REG_r0, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r1) == YARN_REG_r1, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r16) == YARN_REG_r16, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r17) == YARN_REG_r17, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r18) == YARN_REG_r18, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r19) == YARN_REG_r19, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r20) == YARN_REG_r20, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r21) == YARN_REG_r21, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r22) == YARN_REG_r22, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r23) == YARN_REG_r23, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r24) == YARN_REG_r24, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r25) == YARN_REG_r25, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r26) == YARN_REG_r26, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r27) == YARN_REG_r27, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r28) == YARN_REG_r28, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v8) == YARN_REG_v8, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v9) == YARN_REG_v9, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v10) == YARN_REG_v10, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v11) == YARN_REG_v11, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v12) == YARN_REG_v12, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v13) == YARN_REG_v13, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v14) == YARN_REG_v14, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v15) == YARN_REG_v15, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, SP) == YARN_REG_SP, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, LR) == YARN_REG_LR, "Bad register offset");
#endif // __cplusplus
#endif // BUILD_ASM
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(__aarch64__)
#include "OSFiber_asm_aarch64.h"
// void yarn_fiber_swap(yarn_fiber_context* from, const yarn_fiber_context* to)
// x0: from
// x1: to
.text
.global YARN_ASM_SYMBOL(yarn_fiber_swap)
.align 4
YARN_ASM_SYMBOL(yarn_fiber_swap):
// Save context 'from'
// TODO: pairs of str can be combined with stp.
// Store special purpose registers
str x16, [x0, #YARN_REG_r16]
str x17, [x0, #YARN_REG_r17]
str x18, [x0, #YARN_REG_r18]
// Store callee-preserved registers
str x19, [x0, #YARN_REG_r19]
str x20, [x0, #YARN_REG_r20]
str x21, [x0, #YARN_REG_r21]
str x22, [x0, #YARN_REG_r22]
str x23, [x0, #YARN_REG_r23]
str x24, [x0, #YARN_REG_r24]
str x25, [x0, #YARN_REG_r25]
str x26, [x0, #YARN_REG_r26]
str x27, [x0, #YARN_REG_r27]
str x28, [x0, #YARN_REG_r28]
str d8, [x0, #YARN_REG_v8]
str d9, [x0, #YARN_REG_v9]
str d10, [x0, #YARN_REG_v10]
str d11, [x0, #YARN_REG_v11]
str d12, [x0, #YARN_REG_v12]
str d13, [x0, #YARN_REG_v13]
str d14, [x0, #YARN_REG_v14]
str d15, [x0, #YARN_REG_v15]
// Store sp and lr
mov x2, sp
str x2, [x0, #YARN_REG_SP]
str x30, [x0, #YARN_REG_LR]
// Load context 'to'
mov x7, x1
// Load special purpose registers
ldr x16, [x7, #YARN_REG_r16]
ldr x17, [x7, #YARN_REG_r17]
ldr x18, [x7, #YARN_REG_r18]
// Load callee-preserved registers
ldr x19, [x7, #YARN_REG_r19]
ldr x20, [x7, #YARN_REG_r20]
ldr x21, [x7, #YARN_REG_r21]
ldr x22, [x7, #YARN_REG_r22]
ldr x23, [x7, #YARN_REG_r23]
ldr x24, [x7, #YARN_REG_r24]
ldr x25, [x7, #YARN_REG_r25]
ldr x26, [x7, #YARN_REG_r26]
ldr x27, [x7, #YARN_REG_r27]
ldr x28, [x7, #YARN_REG_r28]
ldr d8, [x7, #YARN_REG_v8]
ldr d9, [x7, #YARN_REG_v9]
ldr d10, [x7, #YARN_REG_v10]
ldr d11, [x7, #YARN_REG_v11]
ldr d12, [x7, #YARN_REG_v12]
ldr d13, [x7, #YARN_REG_v13]
ldr d14, [x7, #YARN_REG_v14]
ldr d15, [x7, #YARN_REG_v15]
// Load parameter registers
ldr x0, [x7, #YARN_REG_r0]
ldr x1, [x7, #YARN_REG_r1]
// Load sp and lr
ldr x30, [x7, #YARN_REG_LR]
ldr x2, [x7, #YARN_REG_SP]
mov sp, x2
ret
#endif // defined(__aarch64__)
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(__arm__)
#include "OSFiber_asm_arm.h"
void yarn_fiber_trampoline(void(*target)(void*), void* arg)
{
target(arg);
}
void yarn_fiber_set_target(struct yarn_fiber_context* ctx, void* stack, uint32_t stack_size, void(*target)(void*), void* arg)
{
uintptr_t* stack_top = (uintptr_t*)((uint8_t*)(stack) + stack_size);
ctx->LR = (uintptr_t)&yarn_fiber_trampoline;
ctx->r0 = (uintptr_t)target;
ctx->r1 = (uintptr_t)arg;
ctx->SP = ((uintptr_t)stack_top) & ~(uintptr_t)15;
}
#endif // defined(__arm__)
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#define YARN_REG_r0 0x00
#define YARN_REG_r1 0x04
#define YARN_REG_r12 0x08
#define YARN_REG_r4 0x0c
#define YARN_REG_r5 0x10
#define YARN_REG_r6 0x14
#define YARN_REG_r7 0x18
#define YARN_REG_r8 0x1c
#define YARN_REG_r9 0x20
#define YARN_REG_r10 0x24
#define YARN_REG_r11 0x28
#define YARN_REG_v8 0x2c
#define YARN_REG_v9 0x30
#define YARN_REG_v10 0x34
#define YARN_REG_v11 0x38
#define YARN_REG_v12 0x3c
#define YARN_REG_v13 0x40
#define YARN_REG_v14 0x44
#define YARN_REG_v15 0x48
#define YARN_REG_SP 0x4c
#define YARN_REG_LR 0x50
#ifndef BUILD_ASM
#include <stdint.h>
// Procedure Call Standard for the ARM 64-bit Architecture
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
struct yarn_fiber_context
{
// parameter registers
uintptr_t r0;
uintptr_t r1;
// special purpose registers
uintptr_t r12; // Intra-Procedure-call
// callee-saved registers
uintptr_t r4;
uintptr_t r5;
uintptr_t r6;
uintptr_t r7;
uintptr_t r8;
uintptr_t r9;
uintptr_t r10;
uintptr_t r11;
uintptr_t v8;
uintptr_t v9;
uintptr_t v10;
uintptr_t v11;
uintptr_t v12;
uintptr_t v13;
uintptr_t v14;
uintptr_t v15;
uintptr_t SP; // stack pointer (r13)
uintptr_t LR; // link register (r14)
};
#ifdef __cplusplus
#include <cstddef>
static_assert(offsetof(yarn_fiber_context, r0) == YARN_REG_r0, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r1) == YARN_REG_r1, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r12) == YARN_REG_r12, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r4) == YARN_REG_r4, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r5) == YARN_REG_r5, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r6) == YARN_REG_r6, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r7) == YARN_REG_r7, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r8) == YARN_REG_r8, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r9) == YARN_REG_r9, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r10) == YARN_REG_r10, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, r11) == YARN_REG_r11, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v8) == YARN_REG_v8, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v9) == YARN_REG_v9, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v10) == YARN_REG_v10, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v11) == YARN_REG_v11, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v12) == YARN_REG_v12, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v13) == YARN_REG_v13, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v14) == YARN_REG_v14, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, v15) == YARN_REG_v15, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, SP) == YARN_REG_SP, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, LR) == YARN_REG_LR, "Bad register offset");
#endif // __cplusplus
#endif // BUILD_ASM
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(__arm__)
#include "OSFiber_asm_arm.h"
// void yarn_fiber_swap(yarn_fiber_context* from, const yarn_fiber_context* to)
// x0: from
// x1: to
.text
.global yarn_fiber_swap
.align 4
yarn_fiber_swap:
// Save context 'from'
// TODO: multiple registers can be stored in a single instruction with: stm rA, {rB-rC}
// Store special purpose registers
str r12, [r0, #YARN_REG_r12]
// Store callee-preserved registers
str r4, [r0, #YARN_REG_r4]
str r5, [r0, #YARN_REG_r5]
str r6, [r0, #YARN_REG_r6]
str r7, [r0, #YARN_REG_r7]
str r8, [r0, #YARN_REG_r8]
str r9, [r0, #YARN_REG_r9]
str r10, [r0, #YARN_REG_r10]
str r11, [r0, #YARN_REG_r11]
// Store sp, lr and pc
str sp, [r0, #YARN_REG_SP]
str lr, [r0, #YARN_REG_LR]
// Load context 'to'
// TODO: multiple registers can be loaded in a single instruction with: ldm rA, {rB-rC}
mov r3, r1
// Load special purpose registers
ldr r12, [r3, #YARN_REG_r12]
// Load callee-preserved registers
ldr r4, [r3, #YARN_REG_r4]
ldr r5, [r3, #YARN_REG_r5]
ldr r6, [r3, #YARN_REG_r6]
ldr r7, [r3, #YARN_REG_r7]
ldr r8, [r3, #YARN_REG_r8]
ldr r9, [r3, #YARN_REG_r9]
ldr r10, [r3, #YARN_REG_r10]
ldr r11, [r3, #YARN_REG_r11]
// Load parameter registers
ldr r0, [r3, #YARN_REG_r0]
ldr r1, [r3, #YARN_REG_r1]
// Load sp, lr and pc
ldr sp, [r3, #YARN_REG_SP]
ldr lr, [r3, #YARN_REG_LR]
mov pc, lr
#endif // defined(__arm__)
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(__x86_64__)
#include "OSFiber_asm_x64.h"
void yarn_fiber_trampoline(void(*target)(void*), void* arg)
{
target(arg);
}
void yarn_fiber_set_target(struct yarn_fiber_context* ctx, void* stack, uint32_t stack_size, void(*target)(void*), void* arg)
{
uintptr_t* stack_top = (uintptr_t*)((uint8_t*)(stack) + stack_size);
ctx->RIP = (uintptr_t)&yarn_fiber_trampoline;
ctx->RDI = (uintptr_t)target;
ctx->RSI = (uintptr_t)arg;
ctx->RSP = (uintptr_t)&stack_top[-3];
stack_top[-2] = 0; // No return target.
}
#endif // defined(__x86_64__)
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#define YARN_REG_RBX 0x00
#define YARN_REG_RBP 0x08
#define YARN_REG_R12 0x10
#define YARN_REG_R13 0x18
#define YARN_REG_R14 0x20
#define YARN_REG_R15 0x28
#define YARN_REG_RDI 0x30
#define YARN_REG_RSI 0x38
#define YARN_REG_RSP 0x40
#define YARN_REG_RIP 0x48
#if defined(__APPLE__)
#define YARN_ASM_SYMBOL(x) _##x
#else
#define YARN_ASM_SYMBOL(x) x
#endif
#ifndef BUILD_ASM
#include <stdint.h>
struct yarn_fiber_context
{
// callee-saved registers
uintptr_t RBX;
uintptr_t RBP;
uintptr_t R12;
uintptr_t R13;
uintptr_t R14;
uintptr_t R15;
// parameter registers
uintptr_t RDI;
uintptr_t RSI;
// stack and instruction registers
uintptr_t RSP;
uintptr_t RIP;
};
#ifdef __cplusplus
#include <cstddef>
static_assert(offsetof(yarn_fiber_context, RBX) == YARN_REG_RBX, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, RBP) == YARN_REG_RBP, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, R12) == YARN_REG_R12, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, R13) == YARN_REG_R13, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, R14) == YARN_REG_R14, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, R15) == YARN_REG_R15, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, RDI) == YARN_REG_RDI, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, RSI) == YARN_REG_RSI, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, RSP) == YARN_REG_RSP, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, RIP) == YARN_REG_RIP, "Bad register offset");
#endif // __cplusplus
#endif // BUILD_ASM
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(__x86_64__)
#include "OSFiber_asm_x64.h"
// void yarn_fiber_swap(yarn_fiber_context* from, const yarn_fiber_context* to)
// rdi: from
// rsi: to
.text
.global YARN_ASM_SYMBOL(yarn_fiber_swap)
.align 4
YARN_ASM_SYMBOL(yarn_fiber_swap):
// Save context 'from'
// Store callee-preserved registers
movq %rbx, YARN_REG_RBX(%rdi)
movq %rbp, YARN_REG_RBP(%rdi)
movq %r12, YARN_REG_R12(%rdi)
movq %r13, YARN_REG_R13(%rdi)
movq %r14, YARN_REG_R14(%rdi)
movq %r15, YARN_REG_R15(%rdi)
movq (%rsp), %rcx /* call stores the return address on the stack before jumping */
movq %rcx, YARN_REG_RIP(%rdi)
leaq 8(%rsp), %rcx /* skip the pushed return address */
movq %rcx, YARN_REG_RSP(%rdi)
// Load context 'to'
movq %rsi, %r8
// Load callee-preserved registers
movq YARN_REG_RBX(%r8), %rbx
movq YARN_REG_RBP(%r8), %rbp
movq YARN_REG_R12(%r8), %r12
movq YARN_REG_R13(%r8), %r13
movq YARN_REG_R14(%r8), %r14
movq YARN_REG_R15(%r8), %r15
// Load first two call parameters
movq YARN_REG_RDI(%r8), %rdi
movq YARN_REG_RSI(%r8), %rsi
// Load stack pointer
movq YARN_REG_RSP(%r8), %rsp
// Load instruction pointer, and jump
movq YARN_REG_RIP(%r8), %rcx
jmp *%rcx
#endif // defined(__x86_64__)
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(__i386__)
#include "OSFiber_asm_x86.h"
void yarn_fiber_trampoline(void(*target)(void*), void* arg)
{
target(arg);
}
void yarn_fiber_set_target(struct yarn_fiber_context* ctx, void* stack, uint32_t stack_size, void(*target)(void*), void* arg)
{
uintptr_t* stack_top = (uintptr_t*)((uint8_t*)(stack) + stack_size);
ctx->EIP = (uintptr_t)&yarn_fiber_trampoline;
ctx->ESP = (uintptr_t)&stack_top[-3];
stack_top[-1] = (uintptr_t)arg;
stack_top[-2] = (uintptr_t)target;
stack_top[-3] = 0; // No return target.
}
#endif // defined(__i386__)
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#define YARN_REG_EBX 0x00
#define YARN_REG_EBP 0x04
#define YARN_REG_ESI 0x08
#define YARN_REG_EDI 0x0c
#define YARN_REG_ESP 0x10
#define YARN_REG_EIP 0x14
#ifndef BUILD_ASM
#include <stdint.h>
// Assumes cdecl calling convention.
// Registers EAX, ECX, and EDX are caller-saved, and the rest are callee-saved.
struct yarn_fiber_context
{
// callee-saved registers
uintptr_t EBX;
uintptr_t EBP;
uintptr_t ESI;
uintptr_t EDI;
// stack and instruction registers
uintptr_t ESP;
uintptr_t EIP;
};
#ifdef __cplusplus
#include <cstddef>
static_assert(offsetof(yarn_fiber_context, EBX) == YARN_REG_EBX, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, EBP) == YARN_REG_EBP, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, ESI) == YARN_REG_ESI, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, EDI) == YARN_REG_EDI, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, ESP) == YARN_REG_ESP, "Bad register offset");
static_assert(offsetof(yarn_fiber_context, EIP) == YARN_REG_EIP, "Bad register offset");
#endif // __cplusplus
#endif // BUILD_ASM
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(__i386__)
#include "OSFiber_asm_x86.h"
// void yarn_fiber_swap(yarn_fiber_context* from, const yarn_fiber_context* to)
// esp+4: from
// esp+8: to
.text
.global yarn_fiber_swap
.align 4
yarn_fiber_swap:
// Save context 'from'
movl 4(%esp), %eax
// Store callee-preserved registers
movl %ebx, YARN_REG_EBX(%eax)
movl %ebp, YARN_REG_EBP(%eax)
movl %esi, YARN_REG_ESI(%eax)
movl %edi, YARN_REG_EDI(%eax)
movl (%esp), %ecx /* call stores the return address on the stack before jumping */
movl %ecx, YARN_REG_EIP(%eax)
lea 4(%esp), %ecx /* skip the pushed return address */
movl %ecx, YARN_REG_ESP(%eax)
// Load context 'to'
movl 8(%esp), %ecx
// Load callee-preserved registers
movl YARN_REG_EBX(%ecx), %ebx
movl YARN_REG_EBP(%ecx), %ebp
movl YARN_REG_ESI(%ecx), %esi
movl YARN_REG_EDI(%ecx), %edi
// Load stack pointer
movl YARN_REG_ESP(%ecx), %esp
// Load instruction pointer, and jump
movl YARN_REG_EIP(%ecx), %ecx
jmp *%ecx
#endif // defined(__i386__)
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "OSFiber.hpp"
#include "Yarn_test.hpp"
TEST(WithoutBoundScheduler, OSFiber)
{
std::string str;
auto constexpr fiberStackSize = 8 * 1024;
auto main = std::unique_ptr<yarn::OSFiber>(yarn::OSFiber::createFiberFromCurrentThread());
std::unique_ptr<yarn::OSFiber> fiberA, fiberB, fiberC;
fiberC = std::unique_ptr<yarn::OSFiber>(yarn::OSFiber::createFiber(fiberStackSize, [&]
{
str += "C";
fiberC->switchTo(fiberB.get());
}));
fiberB = std::unique_ptr<yarn::OSFiber>(yarn::OSFiber::createFiber(fiberStackSize, [&]
{
str += "B";
fiberB->switchTo(fiberA.get());
}));
fiberA = std::unique_ptr<yarn::OSFiber>(yarn::OSFiber::createFiber(fiberStackSize, [&]
{
str += "A";
fiberA->switchTo(main.get());
}));
main->switchTo(fiberC.get());
ASSERT_EQ(str, "CBA");
}
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if !defined(_XOPEN_SOURCE)
// This must come before other #includes, otherwise we'll end up with ucontext_t
// definition mismatches, leading to memory corruption hilarity.
#define _XOPEN_SOURCE
#endif // !defined(_XOPEN_SOURCE)
#include "Debug.hpp"
#include <functional>
#include <memory>
#include <ucontext.h>
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif // defined(__clang__)
namespace yarn {
class OSFiber
{
public:
// createFiberFromCurrentThread() returns a fiber created from the current
// thread.
static inline OSFiber* createFiberFromCurrentThread();
// createFiber() returns a new fiber with the given stack size that will
// call func when switched to. func() must end by switching back to another
// fiber, and must not return.
static inline OSFiber* createFiber(size_t stackSize, const std::function<void()>& func);
// switchTo() immediately switches execution to the given fiber.
// switchTo() must be called on the currently executing fiber.
inline void switchTo(OSFiber*);
private:
std::unique_ptr<uint8_t[]> stack;
ucontext_t context;
std::function<void()> target;
};
OSFiber* OSFiber::createFiberFromCurrentThread()
{
auto out = new OSFiber();
out->context = {};
getcontext(&out->context);
return out;
}
OSFiber* OSFiber::createFiber(size_t stackSize, const std::function<void()>& func)
{
union Args
{
OSFiber* self;
struct { int a; int b; };
};
struct Target
{
static void Main(int a, int b)
{
Args u;
u.a = a; u.b = b;
std::function<void()> func;
std::swap(func, u.self->target);
func();
}
};
auto out = new OSFiber();
out->context = {};
out->stack = std::unique_ptr<uint8_t[]>(new uint8_t[stackSize]);
out->target = func;
auto alignmentOffset = 15 - (reinterpret_cast<uintptr_t>(out->stack.get() + 15) & 15);
auto res = getcontext(&out->context);
YARN_ASSERT(res == 0, "getcontext() returned %d", int(res));
out->context.uc_stack.ss_sp = out->stack.get() + alignmentOffset;
out->context.uc_stack.ss_size = stackSize - alignmentOffset;
out->context.uc_link = nullptr;
Args args;
args.self = out;
makecontext(&out->context, reinterpret_cast<void(*)()>(&Target::Main), 2, args.a, args.b);
return out;
}
void OSFiber::switchTo(OSFiber* fiber)
{
auto res = swapcontext(&context, &fiber->context);
YARN_ASSERT(res == 0, "swapcontext() returned %d", int(res));
}
} // namespace yarn
#if defined(__clang__)
#pragma clang diagnostic pop
#endif // defined(__clang__)
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <functional>
#include <memory>
#include <Windows.h>
namespace yarn {
class OSFiber
{
public:
inline ~OSFiber();
// createFiberFromCurrentThread() returns a fiber created from the current
// thread.
static inline OSFiber* createFiberFromCurrentThread();
// createFiber() returns a new fiber with the given stack size that will
// call func when switched to. func() must end by switching back to another
// fiber, and must not return.
static inline OSFiber* createFiber(size_t stackSize, const std::function<void()>& func);
// switchTo() immediately switches execution to the given fiber.
// switchTo() must be called on the currently executing fiber.
inline void switchTo(OSFiber*);
private:
static inline void WINAPI run(void* self);
LPVOID fiber = nullptr;
bool isFiberFromThread = false;
std::function<void()> target;
};
OSFiber::~OSFiber()
{
if (fiber != nullptr)
{
if (isFiberFromThread)
{
ConvertFiberToThread();
}
else
{
DeleteFiber(fiber);
}
}
}
OSFiber* OSFiber::createFiberFromCurrentThread()
{
auto out = new OSFiber();
out->fiber = ConvertThreadToFiber(nullptr);
out->isFiberFromThread = true;
return out;
}
OSFiber* OSFiber::createFiber(size_t stackSize, const std::function<void()>& func)
{
auto out = new OSFiber();
out->fiber = CreateFiber(stackSize, &OSFiber::run, out);
out->target = func;
return out;
}
void OSFiber::switchTo(OSFiber* fiber)
{
SwitchToFiber(fiber->fiber);
}
void WINAPI OSFiber::run(void* self)
{
std::function<void()> func;
std::swap(func, reinterpret_cast<OSFiber*>(self)->target);
func();
}
} // namespace yarn
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment