hw: enable eager FPU context switch for ARM

* Add an ieee754 FPU test
* Remove simple fpu test

Fix #2822
This commit is contained in:
Stefan Kalkowski 2018-05-14 11:30:24 +02:00 committed by Christian Helmuth
parent 4b4247f412
commit d7fa4cfb8b
40 changed files with 2427 additions and 528 deletions

View File

@ -51,6 +51,11 @@ struct Genode::Vm_state : Genode::Cpu_state_modes
Genode::uint32_t tls3;
Genode::uint32_t cpacr;
/**
* Fpu registers
*/
Genode::uint32_t fpscr;
Genode::uint64_t d0_d31[32];
/**
* Timer related registers

View File

@ -31,6 +31,13 @@ struct Genode::Vm_state : Genode::Cpu_state_modes
Genode::addr_t dfar;
Genode::addr_t ttbr[2];
Genode::addr_t ttbrc;
/**
* Fpu registers
*/
Genode::uint32_t fpscr;
Genode::uint64_t d0_d31[32];
Genode::addr_t irq_injection;
};

View File

@ -16,5 +16,7 @@ SRC_CC += spec/arm/kernel/thread_update_pd.cc
SRC_CC += kernel/vm_thread_off.cc
SRC_CC += kernel/kernel.cc
SRC_S += spec/arm/vfpv2.s
# include less specific configuration
include $(BASE_DIR)/../base-hw/lib/mk/spec/arm/core-hw.inc

View File

@ -11,5 +11,7 @@ INC_DIR += $(BASE_DIR)/../base-hw/src/core/spec/arm_v7
SRC_CC += spec/arm_v7/cpu.cc
SRC_CC += spec/arm_v7/perf_counter.cc
SRC_S += spec/arm/vfpv3-d32.cc
# include less specific configuration
include $(BASE_DIR)/../base-hw/lib/mk/spec/arm/core-hw.inc

View File

@ -13,6 +13,6 @@ NR_OF_CPUS = 2
# we need more specific compiler hints for some 'special' assembly code
# override -march=armv7-a because it conflicts with -mcpu=cortex-a15
#
CC_MARCH = -mcpu=cortex-a15
CC_MARCH = -mcpu=cortex-a15 -mfpu=vfpv3 -mfloat-abi=softfp
include $(REP_DIR)/lib/mk/bootstrap-hw.inc

View File

@ -25,7 +25,7 @@ NR_OF_CPUS = 2
# we need more specific compiler hints for some 'special' assembly code
# override -march=armv7-a because it conflicts with -mcpu=cortex-a15
#
CC_MARCH = -mcpu=cortex-a15
CC_MARCH = -mcpu=cortex-a15 -mfpu=vfpv3 -mfloat-abi=softfp
# include less specific configuration
include $(REP_DIR)/lib/mk/spec/exynos5/core-hw.inc

View File

@ -10,7 +10,6 @@ INC_DIR += $(BASE_DIR)/../base-hw/src/core/spec/arm_gic
# add C++ sources
SRC_CC += spec/cortex_a9/kernel/cpu.cc
SRC_CC += spec/cortex_a9/fpu.cc
SRC_CC += spec/cortex_a9/board.cc
SRC_CC += spec/cortex_a9/timer.cc
SRC_CC += spec/arm/smp/kernel/thread_update_pd.cc

View File

@ -9,6 +9,6 @@ SRC_CC += hw/spec/arm/arm_v7_cpu.cc
SRC_CC += hw/spec/32bit/memory_map.cc
SRC_S += bootstrap/spec/arm/crt0.s
CC_MARCH = -mcpu=cortex-a9
CC_MARCH = -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=softfp
include $(BASE_DIR)/../base-hw/lib/mk/bootstrap-hw.inc

View File

@ -13,7 +13,7 @@ SRC_CC += platform_services.cc
NR_OF_CPUS += 2
CC_MARCH = -mcpu=cortex-a9
CC_MARCH = -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=softfp
# include less specific configuration
include $(REP_DIR)/lib/mk/spec/cortex_a9/core-hw.inc

View File

@ -62,6 +62,22 @@
add sp, r0, r1
/****************
** Enable VFP **
****************/
mov r0, #0xf
lsl r0, #20
mcr p15, 0, r0, c1, c0, 2 /* write to CPACR to enable VFP access */
mcr p15, 0, r0, c7, c5, 4 /* deprecated ISB instruction <= ARMv6 */
vmrs r0, fpexc /* enable the VFP by read/write fpexc */
mov r1, #1 /* enable bit 30 */
lsl r1, #30
orr r0, r1
vmsr fpexc, r0
/************************************
** Jump to high-level entry point **
************************************/

View File

@ -58,7 +58,13 @@ struct Genode::Arm_cpu : public Hw::Arm_cpu
}
};
struct alignas(4) Context : Cpu_state
struct Fpu_context
{
uint32_t fpscr { 1UL << 24 }; /* VFP/SIMD - status/control register */
uint64_t d0_d31[32]; /* VFP/SIMD - general purpose registers */
};
struct alignas(4) Context : Cpu_state, Fpu_context
{
Context(bool privileged);
};
@ -146,8 +152,6 @@ struct Genode::Arm_cpu : public Hw::Arm_cpu
** Dummies **
*************/
bool retry_undefined_instr(Context&) { return false; }
/**
* Return kernel name of the executing CPU
*/

View File

@ -115,6 +115,17 @@
stmia r0!, {r1-r3} /* save pc, cpsr and exception type */
clrex /* clear exclusive access needed for cmpxchg */
cps #SVC_MODE
mov r1, #1 /* clear exception state of the VFP */
lsl r1, #30
vmsr fpexc, r1
adr r1, _fpu_save
ldr r1, [r1]
blx r1
/*
* Go to kernel entry code
*/
adr lr, _kernel_entry
ldr lr, [lr]
bx lr
@ -122,6 +133,9 @@
_kernel_entry:
.long kernel
_fpu_save:
.long vfp_save_fpu_context
.section .text
@ -133,3 +147,21 @@
idle_thread_main:
wfi
b idle_thread_main
/*****************************
** kernel to userland switch **
*******************************/
.global kernel_to_user_context_switch
kernel_to_user_context_switch:
push { r0 }
mov r0, r1
bl vfp_load_fpu_context
pop { r0 }
mov sp, r0
ldr lr, [sp, #15*4]
ldr r1, [sp, #16*4]
msr spsr_cxsf, r1
ldm sp, {r0-r14}^
subs pc, lr, #0

View File

@ -1,255 +0,0 @@
/*
* \brief ARM-specific FPU driver for core
* \author Stefan Kalkowski
* \author Martin stein
* \date 2016-01-19
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CORE__SPEC__ARM__FPU_H_
#define _CORE__SPEC__ARM__FPU_H_
#include <util/register.h>
namespace Genode { class Fpu; }
/**
* FPU driver for the ARM VFPv3-D16 architecture
*/
class Genode::Fpu
{
private:
/**
* Floating-point Status and Control Register
*/
struct Fpscr : Register<32>
{
/**
* Read register value
*/
static access_t read()
{
/* FIXME: See annotation 1. */
access_t v;
asm volatile ("mrc p10, 7, %[v], cr1, cr0, 0" : [v] "=r" (v) ::);
return v;
}
/**
* Override register value
*
* \param v write value
*/
static void write(access_t const v)
{
/* FIXME: See annotation 1. */
asm volatile ("mcr p10, 7, %[v], cr1, cr0, 0" :: [v] "r" (v) :);
}
};
/**
* Floating-Point Exception Control register
*/
struct Fpexc : Register<32>
{
struct En : Bitfield<30, 1> { };
/**
* Read register value
*/
static access_t read()
{
/* FIXME: See annotation 1. */
access_t v;
asm volatile ("mrc p10, 7, %[v], cr8, cr0, 0" : [v] "=r" (v) ::);
return v;
}
/**
* Override register value
*
* \param v write value
*/
static void write(access_t const v)
{
/* FIXME: See annotation 1. */
asm volatile ("mcr p10, 7, %[v], cr8, cr0, 0" :: [v] "r" (v) :);
}
};
public:
class Context
{
private:
/*
* Noncopyable
*/
Context(Context const &);
Context &operator = (Context const &);
friend class Fpu;
struct
{
/* advanced FP/SIMD - system registers */
uint32_t fpscr;
uint32_t fpexc;
/* advanced FP/SIMD - general purpose registers d0-d15 */
uint64_t d0, d1, d2, d3, d4, d5, d6, d7;
uint64_t d8, d9, d10, d11, d12, d13, d14, d15;
};
Fpu * _fpu = nullptr;
public:
Context() : fpexc(Fpexc::En::bits(1)) { }
~Context() { if (_fpu) _fpu->unset(*this); }
};
private:
Context * _context = nullptr;
/**
* Enable FPU
*/
void _enable()
{
Fpexc::access_t fpexc = Fpexc::read();
Fpexc::En::set(fpexc, 1);
Fpexc::write(fpexc);
}
/**
* Disable FPU
*/
void _disable()
{
Fpexc::access_t fpexc = Fpexc::read();
Fpexc::En::set(fpexc, 0);
Fpexc::write(fpexc);
}
/**
* Save FPU context
*/
void _save()
{
/* save system registers */
_context->fpexc = Fpexc::read();
_context->fpscr = Fpscr::read();
/*
* Save D0 - D15
*
* FIXME: See annotation 2.
*/
void * const d0_d15_base = &_context->d0;
asm volatile (
"stc p11, cr0, [%[d0_d15_base]], #128"
:: [d0_d15_base] "r" (d0_d15_base) : );
}
/**
* Load context to FPU
*/
void _load()
{
/* load system registers */
Fpexc::write(_context->fpexc);
Fpscr::write(_context->fpscr);
/*
* Load D0 - D15
*
* FIXME: See annotation 2.
*/
void * const d0_d15_base = &_context->d0;
asm volatile (
"ldc p11, cr0, [%[d0_d15_base]], #128"
:: [d0_d15_base] "r" (d0_d15_base) : );
}
/**
* Return wether the FPU is enabled
*/
bool _enabled()
{
Fpexc::access_t fpexc = Fpexc::read();
return Fpexc::En::get(fpexc);
}
public:
/**
* Initialize FPU
*/
void init();
void switch_to(Context & context)
{
if (_context == &context) return;
_disable();
}
/**
* Return wether the FPU fault can be solved
*
* \param state CPU state of the user
*/
bool fault(Context & context)
{
if (_enabled()) { return false; }
_enable();
if (_context != &context) {
if (_context) {
_save();
_context->_fpu = nullptr;
}
_context = &context;
_context->_fpu = this;
_load();
}
return true;
}
/**
* Unset FPU context
*/
void unset(Context &context) {
if (_context == &context) _context = nullptr; }
};
/*
* Annotation 1
*
* According to the ARMv7 manual this should be done via vmsr/vmrs instruction
* but it seems that binutils 2.22 doesn't fully support this yet. Hence, we
* use a co-processor instruction instead. The parameters to target the
* register this way can be determined via 'sys/arm/include/vfp.h' and
* 'sys/arm/arm/vfp.c' of the FreeBSD head branch as from 2014.04.17.
*/
/*
* Annotation 2
*
* According to the ARMv7 manual this should be done via vldm/vstm instruction
* but it seems that binutils 2.22 doesn't fully support this yet. Hence, we
* use a co-processor instruction instead. The parameters to target the
* register this way can be determined via 'sys/arm/arm/vfp.c' of the FreeBSD
* head branch as from 2014.04.17.
*/
#endif /* _CORE__SPEC__ARM__FPU_H_ */

View File

@ -19,6 +19,9 @@
using namespace Kernel;
extern "C" void kernel_to_user_context_switch(Cpu::Context*, Cpu::Fpu_context*);
void Thread::exception(Cpu & cpu)
{
switch (regs->cpu_exception) {
@ -34,7 +37,6 @@ void Thread::exception(Cpu & cpu)
_interrupt(cpu.id());
return;
case Cpu::Context::UNDEFINED_INSTRUCTION:
if (_cpu->retry_undefined_instr(*regs)) { return; }
Genode::warning(*this, ": undefined instruction at ip=",
Genode::Hex(regs->ip));
_die();
@ -106,14 +108,8 @@ void Thread::proceed(Cpu & cpu)
cpu.switch_to(*regs, pd()->mmu_regs);
regs->cpu_exception = cpu.stack_start();
asm volatile("mov sp, %0 \n"
"msr spsr_cxsf, %1 \n"
"mov lr, %2 \n"
"ldm sp, {r0-r14}^ \n"
"subs pc, lr, #0 \n"
:: "r" (static_cast<Cpu::Context*>(&*regs)),
"r" (regs->cpsr), "r" (regs->ip));
kernel_to_user_context_switch((static_cast<Cpu::Context*>(&*regs)),
(static_cast<Cpu::Fpu_context*>(&*regs)));
}

View File

@ -0,0 +1,31 @@
/*
* \brief VFPv2 context load/store
* \author Stefan Kalkowski
* \date 2018-05-06
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
.section .text
.global vfp_save_fpu_context
vfp_save_fpu_context:
vmrs r1, fpscr
stmia r0!, {r1}
vstm r0!, {d0-d15}
mov pc, lr
.global vfp_load_fpu_context
vfp_load_fpu_context:
push { r1, lr }
ldr r1, [r0]
vmsr fpscr, r1
add r1, r0, #4
vldm r1!, {d0-d15}
pop { r1, pc }

View File

@ -0,0 +1,34 @@
/*
* \brief VFPv3-D32 context load/store
* \author Stefan Kalkowski
* \date 2018-05-06
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
.section .text
.global vfp_save_fpu_context
vfp_save_fpu_context:
push { r1, lr }
vmrs r1, fpscr
stmia r0!, {r1}
vstm r0!, {d0-d15}
vstm r0!, {d16-d31}
pop { r1, pc }
.global vfp_load_fpu_context
vfp_load_fpu_context:
push { r1, lr }
ldr r1, [r0]
vmsr fpscr, r1
add r1, r0, #4
vldm r1!, {d0-d15}
vldm r1!, {d16-d31}
pop { r1, pc }

View File

@ -69,10 +69,14 @@ monitor_mode_exception_vector:
_nonsecure_kernel_entry:
ldr lr, [sp, #17*4] /* load kernel sp from vm context */
stmia r0!, {r1-r2} /* save spsr, and exception reason */
mov r1, #1 /* clear exception state of the VFP */
lsl r1, #30
vmsr fpexc, r1
mrc p15, 0, r3, c6, c0, 0 /* move DFAR to r3 */
mrc p15, 0, r4, c2, c0, 0 /* move TTBR0 to r4 */
mrc p15, 0, r5, c2, c0, 1 /* move TTBR1 to r5 */
mrc p15, 0, r6, c2, c0, 2 /* move TTBRC to r6 */
vmrs r7, fpscr /* move FPU ctrl to r7 */
mov r1, #0
mcr p15, 0, r1, c1, c1, 0 /* disable non-secure bit */
.irp mode,27,19,23,18,17 /* save mode specific registers */
@ -81,7 +85,9 @@ monitor_mode_exception_vector:
stmia r0!, {r1,sp,lr} /* store mode-specific sp and lr */
.endr
stmia r0!, {r8-r12} /* save fiq r8-r12 */
stmia r0!, {r3-r6} /* save MMU registers */
stmia r0!, {r3-r7} /* save MMU registers */
vstm r0!, {d0-d15} /* save FPU registers */
vstm r0!, {d16-d31}
cps #22 /* switch back to monitor mode */
mov r0, #0b111010011 /* spsr to SVC mode, irqs masked */
msr spsr_cxsf, r0
@ -109,6 +115,12 @@ monitor_mode_enter_normal_world:
msr spsr_cxfs, r2 /* load mode's spsr */
.endr
ldmia r0!, {r8 - r12} /* load fiq r8-r12 */
add r0, r0, #4*4
ldr r1, [r0]
vmsr fpscr, r1
add r1, r0, #4
vldm r1!, {d0-d15}
vldm r1!, {d16-d31}
cps #22 /* switch to monitor mode */
ldmia sp, {r0-lr}^ /* load user r0-r12,sp,lr */
str lr, [sp, #17*4] /* store kernel sp in vm context */

View File

@ -91,11 +91,16 @@ _host_to_vm:
mcr p15, 0, r10, c6, c0, 0 /* write DFAR */
mcr p15, 0, r11, c6, c0, 2 /* write IFAR */
mcr p15, 0, r12, c13, c0, 1 /* write CIDR */
ldm r0, {r1-r4}
ldm r0!, {r1-r4}
mcr p15, 0, r1, c13, c0, 2 /* write TLS1 */
mcr p15, 0, r2, c13, c0, 3 /* write TLS2 */
mcr p15, 0, r3, c13, c0, 4 /* write TLS3 */
mcr p15, 0, r4, c1, c0, 2 /* write CPACR */
ldr r1, [r0]
vmsr fpscr, r1
add r1, r0, #4
vldm r1!, {d0-d15}
vldm r1!, {d16-d31}
ldmia sp, {r0-r12} /* load vm's r0-r12 */
eret
@ -106,6 +111,8 @@ _vm_to_host:
mcrr p15, 6, r1, r1, c2 /* write VTTBR */
mcr p15, 4, r1, c1, c1, 0 /* write HCR register */
mcr p15, 4, r1, c1, c1, 3 /* write HSTR register */
mov r1, #0xf
lsl r1, #20
mcr p15, 0, r1, c1, c0, 2 /* write CPACR */
mrs r1, ELR_hyp /* read ip */
mrs r2, spsr /* read cpsr */
@ -130,7 +137,15 @@ _vm_to_host:
mrc p15, 0, r10, c13, c0, 2 /* read TLS1 */
mrc p15, 0, r11, c13, c0, 3 /* read TLS2 */
mrc p15, 0, r12, c13, c0, 4 /* read TLS3 */
stm r0, {r3-r12}
stm r0!, {r3-r12}
add r0, r0, #4
mov r3, #1 /* clear fpu exception state */
lsl r3, #30
vmsr fpexc, r3
vmrs r4, fpscr
stmia r0!, {r4}
vstm r0!, {d0-d15}
vstm r0!, {d16-d31}
add r0, sp, #13*4
ldr r3, _vt_host_context_ptr
ldr sp, [r3]

View File

@ -135,13 +135,6 @@ class Genode::Cpu : public Arm_v7_cpu
if (mmu_context.id() && (Ttbr0_64bit::read() != mmu_context.ttbr0))
Ttbr0_64bit::write(mmu_context.ttbr0);
}
/*************
** Dummies **
*************/
bool retry_undefined_instr(Context&) { return false; }
};
#endif /* _CORE__SPEC__CORTEX_A15__CPU_H_ */

View File

@ -16,45 +16,13 @@
#define _CORE__SPEC__CORTEX_A9__CPU_H_
/* core includes */
#include <spec/arm/fpu.h>
#include <spec/arm_v7/cpu_support.h>
#include <board.h>
namespace Genode { class Cpu; }
namespace Genode { struct Cpu; }
class Genode::Cpu : public Arm_v7_cpu
struct Genode::Cpu : Arm_v7_cpu
{
protected:
Fpu _fpu { };
public:
struct Context : Arm_cpu::Context, Fpu::Context
{
Context(bool privileged)
: Arm_cpu::Context(privileged) {}
};
/**
* Next cpu context to switch to
*
* \param context context to switch to
*/
void switch_to(Context & context, Mmu_context & mmu_context)
{
Arm_cpu::switch_to(context, mmu_context);
_fpu.switch_to(context);
}
/**
* Return wether to retry an undefined user instruction after this call
*
* \param state CPU state of the user
*/
bool retry_undefined_instr(Context & context) {
return _fpu.fault(context); }
/**
* Write back dirty cache lines and invalidate whole data cache
*/

View File

@ -1,24 +0,0 @@
/*
* \brief CPU driver for core
* \author Martin stein
* \author Stefan Kalkowski
* \date 2016-01-19
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <cpu.h>
void Genode::Fpu::init()
{
Cpu::Cpacr::access_t cpacr = Cpu::Cpacr::read();
Cpu::Cpacr::Cp10::set(cpacr, 3);
Cpu::Cpacr::Cp11::set(cpacr, 3);
Cpu::Cpacr::write(cpacr);
_disable();
}

View File

@ -23,8 +23,6 @@
void Kernel::Cpu::init(Kernel::Pic &pic)
{
_fpu.init();
{
Lock::Guard guard(data_lock());

View File

@ -116,11 +116,6 @@ class Genode::Cpu : public Hw::X86_64_cpu
Fpu & fpu() { return _fpu; }
/**
* Return wether to retry an undefined user instruction after this call
*/
bool retry_undefined_instr(Context&) { return false; }
/**
* Return kernel name of the executing CPU
*/

View File

@ -1,6 +1,6 @@
SPECS += arm
REP_INC_DIR += include/spec/arm_v6
CC_MARCH ?= -march=armv6k
CC_MARCH ?= -march=armv6k -mfpu=vfp -mfloat-abi=softfp
include $(BASE_DIR)/mk/spec/arm.mk

View File

@ -1,5 +1,5 @@
SPECS += arm_v7
CC_MARCH ?= -march=armv7-a
CC_MARCH ?= -march=armv7-a -mfpu=vfpv3 -mfloat-abi=softfp
include $(BASE_DIR)/mk/spec/arm_v7.mk

View File

@ -1 +0,0 @@
Test pseudo-parallel use of FPU if available

View File

@ -1,2 +0,0 @@
_/src/init
_/src/test-fpu

View File

@ -1 +0,0 @@
2018-11-01 f8565783b90caaa5724a58e6ed7be9c999211fe3

View File

@ -1,43 +0,0 @@
<runtime ram="32M" caps="1000" binary="init">
<events>
<timeout meaning="failed" sec="20" />
<log meaning="succeeded">
[init -> test] FPU user started
[init -> test] FPU user started
[init -> test] FPU user started
[init -> test] FPU user started
[init -> test] FPU user started
[init -> test] FPU user started
[init -> test] FPU user started
[init -> test] FPU user started
[init -> test] FPU user started
[init -> test] FPU user started
[init -> test] test done
</log>
<log meaning="failed">calculation error</log>
</events>
<content>
<rom label="ld.lib.so"/>
<rom label="test-fpu"/>
</content>
<config>
<parent-provides>
<service name="ROM"/>
<service name="CPU"/>
<service name="RM"/>
<service name="PD"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> </any-service>
</default-route>
<default caps="100"/>
<start name="test">
<binary name="test-fpu"/>
<resource name="RAM" quantum="10M"/>
</start>
</config>
</runtime>

View File

@ -1,2 +0,0 @@
SRC_DIR = src/test/fpu
include $(GENODE_DIR)/repos/base/recipes/src/content.inc

View File

@ -1 +0,0 @@
2018-11-01 fc82355e25add8ece1cfe7a4c93b8c42e4866049

View File

@ -1 +0,0 @@
base

View File

@ -1,86 +0,0 @@
/*
* \brief Test pseudo-parallel use of FPU if available
* \author Martin Stein
* \date 2014-04-29
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/component.h>
#include <base/heap.h>
#include <base/log.h>
#include <base/thread.h>
using namespace Genode;
class Fpu_user : public Thread
{
private:
float _x;
Signal_transmitter _st;
Semaphore & _sem;
void _calc(float volatile & x, float volatile & y)
{
for (unsigned j = 0; j < 100; j++) {
x *= (y * 1.357);
x /= (y * 1.246);
}
}
public:
Fpu_user(Env & env, float x, Signal_context_capability c, Semaphore &s)
: Thread(env, "fpu_user", sizeof(size_t)*2048), _x(x), _st(c), _sem(s) {
start(); }
void entry()
{
log("FPU user started");
enum { TRIALS = 1000 };
for (unsigned i = 0; i < TRIALS; i++) {
float volatile a = _x + (float)i * ((float)1 / TRIALS);
float volatile b = _x + (float)i * ((float)1 / TRIALS);
float volatile c = _x;
_calc(a, c);
_calc(b, c);
if (a != b) {
error("calculation error");
break;
}
}
_sem.up();
_st.submit();
}
};
struct Main
{
enum { FPU_USERS = 10 };
Semaphore sem { };
Env & env;
Heap heap { env.ram(), env.rm() };
Signal_handler<Main> handler { env.ep(), *this, &Main::handle };
Main(Env & env) : env(env) {
for (unsigned i = 0; i < FPU_USERS; i++)
new (heap) Fpu_user(env, (i + 1) * 1.234, handler, sem); }
void handle() {
if (sem.cnt() >= FPU_USERS) log("test done"); }
};
void Component::construct(Genode::Env & env) {
static Main main(env); }

View File

@ -1,14 +0,0 @@
#
# \brief Test pseudo-parallel use of FPU if available
# \author Martin Stein
# \date 2012-04-25
#
# Set program name
TARGET = test-fpu
# Add C++ sources
SRC_CC += main.cc
# Add libraries
LIBS += base

View File

@ -44,7 +44,6 @@ set avail_test_pkgs {
test-dynamic_config_slave
test-expat
test-fault_detection
test-fpu
test-fs_log
test-fs_packet
test-fs_report

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
#include <base/heap.h>
#include <libc/component.h>
extern int main (int argc, char* argv[]);
void Libc::Component::construct(Libc::Env &env)
{
env.exec_static_constructors();
Genode::Heap heap(env.ram(), env.rm());
int r = 0;
Libc::with_libc([&r] () { r = main(0, 0); });
env.parent().exit(r);
}

View File

@ -0,0 +1,4 @@
TARGET = test-ieee754
LIBS = libc libm
SRC_C = tst-ieee754.c
SRC_CC = component.cc

View File

@ -0,0 +1,489 @@
/* Some IEEE-754 / ISO C99+ conformance tests.
*
* Compile this program with:
* gcc -Wall -O2 -std=c99 tst-ieee754.c -o tst-ieee754 -lm
* for instance.
*
* Add -DFP_CONTRACT to allow contraction of FP expressions (e.g. with icc).
*
* Copyright 2003-2017 Vincent Lefevre <vincent@vinc17.net>.
*
* You may use this software under the terms of the MIT License:
* http://opensource.org/licenses/MIT
*
* More information: https://en.wikipedia.org/wiki/MIT_License
*/
#define SVNID "$Id: tst-ieee754.c 98609 2017-05-19 18:35:55Z vinc17/zira $"
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#include <float.h>
#include <math.h>
#define STRINGIFY(S) #S
#define MAKE_STR(S) STRINGIFY(S)
#ifdef FP_CONTRACT
#undef FP_CONTRACT
#define FP_CONTRACT "ON"
#pragma STDC FP_CONTRACT ON
#else
#define FP_CONTRACT "OFF"
#pragma STDC FP_CONTRACT OFF
#endif
#ifndef NO_FENV_H
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
#endif
#ifndef NAN
#define NAN (0.0/0.0)
#endif
#ifndef INFINITY
#define INFINITY (1.0/0.0)
#endif
#define DBL_NAN (NAN)
#define DBL_POS_INF (INFINITY)
#define DBL_NEG_INF (- DBL_POS_INF)
/* Note: The dynamic epsilon now gives information about
- the possible extended precision used to evaluate expression;
- the possible reduced precision due to the use of options like
GCC's -mpc32 or -mpc64 to reduce the dynamic rounding precision
with "387" arithmetic on x86 processors. */
#define PREC_EPSILON(T,V,F) \
do \
{ \
volatile T eps = 1.0; \
printf (#V " = %" F "g = %" F "a\n", (T) (V), (T) (V)); \
while (eps != 0) \
{ \
volatile T x = 1.0, e = eps / FLT_RADIX; \
x = (x + e) - 1.0; \
if (x != e) \
break; \
eps = e; \
} \
if (eps == 0) \
printf (" (cannot compute the dynamic epsilon)\n"); \
else if (eps != (V)) \
printf (" (dynamic epsilon = %" F "g = %" F "a)\n", eps, eps); \
} \
while (0)
#define ERRSTR(X) ((X) ? " [ERROR]" : "")
static float flt_max = FLT_MAX;
static double dbl_max = DBL_MAX;
static long double ldbl_max = LDBL_MAX;
static float flt_epsilon = FLT_EPSILON;
static double dbl_epsilon = DBL_EPSILON;
static long double ldbl_epsilon = LDBL_EPSILON;
/* <float.h> constants */
static void float_h (void)
{
printf ("FLT_RADIX = %d\n", (int) FLT_RADIX);
printf ("FLT_MANT_DIG = %d\n", (int) FLT_MANT_DIG);
printf ("DBL_MANT_DIG = %d\n", (int) DBL_MANT_DIG);
printf ("LDBL_MANT_DIG = %d\n\n", (int) LDBL_MANT_DIG);
printf ("FLT_MIN_EXP = %d\n", (int) FLT_MIN_EXP);
printf ("DBL_MIN_EXP = %d\n", (int) DBL_MIN_EXP);
printf ("LDBL_MIN_EXP = %d\n\n", (int) LDBL_MIN_EXP);
printf ("FLT_MAX_EXP = %d\n", (int) FLT_MAX_EXP);
printf ("DBL_MAX_EXP = %d\n", (int) DBL_MAX_EXP);
printf ("LDBL_MAX_EXP = %d\n\n", (int) LDBL_MAX_EXP);
PREC_EPSILON (float, FLT_EPSILON, "");
PREC_EPSILON (double, DBL_EPSILON, "");
PREC_EPSILON (long double, LDBL_EPSILON, "L");
putchar ('\n');
printf ("FLT_MIN = %g = %a\n", (double) FLT_MIN, (double) FLT_MIN);
printf ("DBL_MIN = %g = %a\n", (double) DBL_MIN, (double) DBL_MIN);
printf ("LDBL_MIN = %Lg = %La\n\n",
(long double) LDBL_MIN, (long double) LDBL_MIN);
printf ("FLT_MAX = %g = %a\n", (double) FLT_MAX, (double) FLT_MAX);
printf ("DBL_MAX = %g = %a\n", (double) DBL_MAX, (double) DBL_MAX);
printf ("LDBL_MAX = %Lg = %La\n\n",
(long double) LDBL_MAX, (long double) LDBL_MAX);
}
#define TSIZEOF(T) printf ("sizeof(" #T ") = %d\n", (int) sizeof(T))
static void float_sizeof (void)
{
TSIZEOF (float);
TSIZEOF (double);
TSIZEOF (long double);
putchar ('\n');
}
static void tstcast (void)
{
double x;
x = (double) 0;
printf ("(double) 0 = %g\n", x);
}
/* This mostly tests signed zero support. This test is written in
such a way that "gcc -O -ffast-math" gives a wrong result. */
static void signed_zero_inf (void)
{
double x = 0.0, y = -0.0, px, py, nx, ny;
printf ("Signed zero tests (x is 0.0 and y is -0.0):\n");
if (x == y)
printf (" Test 1.0 / x != 1.0 / y returns %d (should be 1).\n",
1.0 / x != 1.0 / y);
else
printf ("x != y; this is wrong!\n");
px = +x;
if (x == px)
printf (" Test 1.0 / x == 1.0 / +x returns %d (should be 1).\n",
1.0 / x == 1.0 / px);
else
printf ("x != +x; this is wrong!\n");
py = +y;
if (x == py)
printf (" Test 1.0 / x != 1.0 / +y returns %d (should be 1).\n",
1.0 / x != 1.0 / py);
else
printf ("x != +y; this is wrong!\n");
nx = -x;
if (x == nx)
printf (" Test 1.0 / x != 1.0 / -x returns %d (should be 1).\n",
1.0 / x != 1.0 / nx);
else
printf ("x != -x; this is wrong!\n");
ny = -y;
if (x == ny)
printf (" Test 1.0 / x == 1.0 / -y returns %d (should be 1).\n",
1.0 / x == 1.0 / ny);
else
printf ("x != -y; this is wrong!\n");
}
static void tstadd (double x, double y)
{
double a, s;
a = x + y;
s = x - y;
printf ("%g + %g = %g\n", x, y, a);
printf ("%g - %g = %g\n", x, y, s);
}
static void tstmul (double x, double y)
{
double m;
m = x * y;
printf ("%g * %g = %g\n", x, y, m);
}
#define TSTCONST(S,OP) \
printf ("Constant expression 1 " S " DBL_MIN = %.20g\n" \
"Variable expression 1 " S " DBL_MIN = %.20g\n", \
1.0 OP DBL_MIN, 1.0 OP x);
static void tstconst (void)
{
volatile double x = DBL_MIN;
TSTCONST ("+", +);
TSTCONST ("-", -);
}
#define TSTDIV(T,S) \
do \
{ \
volatile T x = 1.0, y = 3.0; \
x /= y; \
printf ("1/3 in %-12s: %" S "a\n", #T, x); \
} \
while (0)
static void tstpow (void)
{
double val[] = { 0.0, 0.0, 0.0, +0.0, -0.0,
+0.5, -0.5, +1.0, -1.0, +2.0, -2.0 };
int i, j;
/* Not used above to avoid an error with IRIX64 cc. */
val[0] = DBL_NAN;
val[1] = DBL_POS_INF;
val[2] = DBL_NEG_INF;
for (i = 0; i < sizeof (val) / sizeof (val[0]); i++)
for (j = 0; j < sizeof (val) / sizeof (val[0]); j++)
{
double p;
p = pow (val[i], val[j]);
printf ("pow(%g, %g) = %g\n", val[i], val[j], p);
}
}
static void tstall (void)
{
float fm = FLT_MAX, fe = FLT_EPSILON;
double dm = DBL_MAX, de = DBL_EPSILON;
long double lm = LDBL_MAX, le = LDBL_EPSILON;
tstcast ();
signed_zero_inf ();
tstadd (+0.0, +0.0);
tstadd (+0.0, -0.0);
tstadd (-0.0, +0.0);
tstadd (-0.0, -0.0);
tstadd (+1.0, +1.0);
tstadd (+1.0, -1.0);
tstmul (+0.0, +0.0);
tstmul (+0.0, -0.0);
tstmul (-0.0, +0.0);
tstmul (-0.0, -0.0);
tstconst ();
TSTDIV (float, "");
TSTDIV (double, "");
TSTDIV (long double, "L");
printf ("Dec 1.1 = %a\n", (double) 1.1);
printf ("FLT_MAX = %a%s\n", (double) fm, ERRSTR (fm != flt_max));
printf ("DBL_MAX = %a%s\n", dm, ERRSTR (dm != dbl_max));
printf ("LDBL_MAX = %La%s\n", lm, ERRSTR (lm != ldbl_max));
printf ("FLT_EPSILON = %a%s\n", (double) fe, ERRSTR (fe != flt_epsilon));
printf ("DBL_EPSILON = %a%s\n", de, ERRSTR (de != dbl_epsilon));
printf ("LDBL_EPSILON = %La%s\n", le, ERRSTR (le != ldbl_epsilon));
tstpow ();
}
static void tsteval_method (void)
{
volatile double x, y, z;
#if __STDC__ == 1 && __STDC_VERSION__ >= 199901 && defined(__STDC_IEC_559__)
printf ("__STDC_IEC_559__ defined:\n"
"The implementation shall conform to the IEEE-754 standard.\n");
# ifdef FLT_EVAL_METHOD
printf ("FLT_EVAL_METHOD is %d (see ISO/IEC 9899, 5.2.4.2.2#8).\n\n",
(int) FLT_EVAL_METHOD);
# else
printf ("But FLT_EVAL_METHOD is not defined!\n\n");
# endif
#endif
x = 9007199254740994.0; /* 2^53 + 2 */
y = 1.0 - 1/65536.0;
z = x + y;
printf ("x + y, with x = 9007199254740994.0 and y = 1.0 - 1/65536.0"
" (type double).\n"
"The IEEE-754 result is 9007199254740994 with double precision.\n"
"The IEEE-754 result is 9007199254740996 with extended precision.\n"
"The obtained result is %.17g.\n", z);
if (z == 9007199254740996.0) /* computations in extended precision */
{
volatile double a, b;
double c;
a = 9007199254740992.0; /* 2^53 */
b = a + 0.25;
c = a + 0.25;
if (b != c)
printf ("\nBUG:\nThe implementation doesn't seem to convert values "
"to the target type after\nan assignment (see ISO/IEC 9899: "
"5.1.2.3#12, 6.3.1.5#2 and 6.3.1.8#2[52]).\n");
}
}
/* This test is useful only on implementations where the "double" type
* corresponds to the IEEE-754 double precision and the "long double"
* type corresponds to the traditional x86 extended precision, but let's
* do it in any case. It shows a bug in gcc 3.4 to 4.3.3 on x86_64 and
* ia64 platforms. See:
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36578
*/
static void ldcast_test (void)
{
volatile double a = 4294967219.0;
volatile double b = 4294967429.0;
double c, d;
long double al, bl;
al = a;
bl = b;
c = (long double) a * (long double) b;
d = al * bl;
if (c != d)
printf ("\nBUG: Casts to long double do not seem to be taken into "
"account when\nthe result to stored to a variable of type "
"double. If your compiler\nis gcc (version < 4.3.4), this "
"may be the following bug:\n "
"https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36578\n");
}
static void tstnan (void)
{
double d;
/* Various tests to detect a NaN without using the math library (-lm).
* MIPSpro 7.3.1.3m (IRIX64) does too many optimisations, so that
* both NAN != NAN and !(NAN >= 0.0 || NAN <= 0.0) give 0 instead
* of 1. As a consequence, in MPFR, one needs to use
* #define DOUBLE_ISNAN(x) (!(((x) >= 0.0) + ((x) <= 0.0)))
* in this case. */
d = NAN;
printf ("\n");
printf ("NAN != NAN --> %d (should be 1)\n", d != d);
printf ("isnan(NAN) --> %d (should be 1)\n", isnan (d));
printf ("NAN >= 0.0 --> %d (should be 0)\n", d >= 0.0);
printf ("NAN <= 0.0 --> %d (should be 0)\n", d <= 0.0);
printf (" #3||#4 --> %d (should be 0)\n", d >= 0.0 || d <= 0.0);
printf ("!(#3||#4) --> %d (should be 1)\n", !(d >= 0.0 || d <= 0.0));
printf (" #3 + #4 --> %d (should be 0)\n", (d >= 0.0) + (d <= 0.0));
printf ("!(#3 + #4) --> %d (should be 1)\n", !((d >= 0.0) + (d <= 0.0)));
}
#define TSTINVALID(F,C) \
do \
{ \
feclearexcept (FE_INVALID); \
(void) (d C 0.0); \
if ((F) ^ ! fetestexcept (FE_INVALID)) \
printf ("The FE_INVALID flag is%s set for NAN " #C " 0.\n", \
(F) ? "" : " not"); \
} \
while (0)
static void tstinvalid (void)
{
#ifdef NO_FENV_H
printf ("The FE_INVALID flag could not be tested (no <fenv.h>)\n");
#else
double d = NAN;
TSTINVALID(1,==);
TSTINVALID(1,!=);
TSTINVALID(0,>=);
TSTINVALID(0,<=);
TSTINVALID(0,>);
TSTINVALID(0,<);
#endif
}
/* Note: we do not use the FP_CONTRACT pragma locally (in a block) as
icc 10.1 seems to disable contraction when it sees FP_CONTRACT OFF
somewhere in the source. */
static void fused_madd_test (void)
{
#define TWO40 (1099511627776.0) /* 2^40 */
#define C1U40 (1.0 + 1.0/TWO40) /* 1 + 2^(-40) */
volatile double x = C1U40, y = C1U40, z = -1.0, d;
d = x * y + z;
printf ("\nx * y + z with FP_CONTRACT " FP_CONTRACT " is %sfused.\n",
d == 2.0 * (1 + 0.5 / TWO40) / TWO40 ? "" : "not ");
}
/* FE_INVALID exception with Clang:
* https://bugs.llvm.org//show_bug.cgi?id=17686
*/
static void double_to_unsigned (void)
{
volatile double d;
uint64_t i = (uint64_t) 1 << 63;
int t1, t2;
d = i;
feclearexcept (FE_INVALID);
t1 = (uint64_t) d != i;
t2 = fetestexcept (FE_INVALID);
if (t1 || t2)
printf ("\nError in cast of double to unsigned: %s value%s\n",
t1 ? "incorrect" : "correct", t2 ? ", FE_INVALID" : "");
}
static void ibm_ldconv (void)
{
#define CAT1(X) 1 ## X
#define CAT2(X) CAT1(X)
#define LD0 .000000000000000000000000000000000001L
#define LD1 CAT2(LD0)
long double x = 1.0L + LD0, y = LD1;
if (x > 1.0L && y == 1.0L)
{
printf ("\nBad conversion of " MAKE_STR(LD1) "\n");
printf ("Got 1 instead of about 1 + %La\n", x - 1.0L);
}
}
int main (void)
{
/* printf ("%s\n\n", SVNID); */
float_h ();
float_sizeof ();
tsteval_method ();
ldcast_test ();
tstnan ();
tstinvalid ();
fused_madd_test ();
double_to_unsigned ();
if (LDBL_MIN_EXP == -968 && LDBL_MAX_EXP == 1024 &&
LDBL_MANT_DIG == 106) /* IBM long double format, i.e. double-double */
ibm_ldconv ();
printf ("\nRounding to nearest\n");
#ifdef FE_TONEAREST
if (fesetround (FE_TONEAREST))
printf ("Error, but let's do the test since it "
"should be the default rounding mode.\n");
#endif
tstall ();
#ifdef FE_TOWARDZERO
printf ("\nRounding toward 0\n");
if (fesetround (FE_TOWARDZERO))
printf ("Error\n");
else
tstall ();
#endif
#ifdef FE_DOWNWARD
printf ("\nRounding to -oo\n");
if (fesetround (FE_DOWNWARD))
printf ("Error\n");
else
tstall ();
#endif
#ifdef FE_UPWARD
printf ("\nRounding to +oo\n");
if (fesetround (FE_UPWARD))
printf ("Error\n");
else
tstall ();
#endif
return 0;
}

View File

@ -12,6 +12,7 @@ fetchurl_lxip
fetchurl_lwip
fs_query
gdb_monitor
ieee754
init_smp
input_filter
libc_fatfs