hw: enable eager FPU context switch for ARM
* Add an ieee754 FPU test * Remove simple fpu test Fix #2822
This commit is contained in:
parent
4b4247f412
commit
d7fa4cfb8b
|
@ -51,6 +51,11 @@ struct Genode::Vm_state : Genode::Cpu_state_modes
|
||||||
Genode::uint32_t tls3;
|
Genode::uint32_t tls3;
|
||||||
Genode::uint32_t cpacr;
|
Genode::uint32_t cpacr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fpu registers
|
||||||
|
*/
|
||||||
|
Genode::uint32_t fpscr;
|
||||||
|
Genode::uint64_t d0_d31[32];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timer related registers
|
* Timer related registers
|
||||||
|
|
|
@ -31,6 +31,13 @@ struct Genode::Vm_state : Genode::Cpu_state_modes
|
||||||
Genode::addr_t dfar;
|
Genode::addr_t dfar;
|
||||||
Genode::addr_t ttbr[2];
|
Genode::addr_t ttbr[2];
|
||||||
Genode::addr_t ttbrc;
|
Genode::addr_t ttbrc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fpu registers
|
||||||
|
*/
|
||||||
|
Genode::uint32_t fpscr;
|
||||||
|
Genode::uint64_t d0_d31[32];
|
||||||
|
|
||||||
Genode::addr_t irq_injection;
|
Genode::addr_t irq_injection;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,5 +16,7 @@ SRC_CC += spec/arm/kernel/thread_update_pd.cc
|
||||||
SRC_CC += kernel/vm_thread_off.cc
|
SRC_CC += kernel/vm_thread_off.cc
|
||||||
SRC_CC += kernel/kernel.cc
|
SRC_CC += kernel/kernel.cc
|
||||||
|
|
||||||
|
SRC_S += spec/arm/vfpv2.s
|
||||||
|
|
||||||
# include less specific configuration
|
# include less specific configuration
|
||||||
include $(BASE_DIR)/../base-hw/lib/mk/spec/arm/core-hw.inc
|
include $(BASE_DIR)/../base-hw/lib/mk/spec/arm/core-hw.inc
|
||||||
|
|
|
@ -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/cpu.cc
|
||||||
SRC_CC += spec/arm_v7/perf_counter.cc
|
SRC_CC += spec/arm_v7/perf_counter.cc
|
||||||
|
|
||||||
|
SRC_S += spec/arm/vfpv3-d32.cc
|
||||||
|
|
||||||
# include less specific configuration
|
# include less specific configuration
|
||||||
include $(BASE_DIR)/../base-hw/lib/mk/spec/arm/core-hw.inc
|
include $(BASE_DIR)/../base-hw/lib/mk/spec/arm/core-hw.inc
|
||||||
|
|
|
@ -13,6 +13,6 @@ NR_OF_CPUS = 2
|
||||||
# we need more specific compiler hints for some 'special' assembly code
|
# we need more specific compiler hints for some 'special' assembly code
|
||||||
# override -march=armv7-a because it conflicts with -mcpu=cortex-a15
|
# 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
|
include $(REP_DIR)/lib/mk/bootstrap-hw.inc
|
||||||
|
|
|
@ -25,7 +25,7 @@ NR_OF_CPUS = 2
|
||||||
# we need more specific compiler hints for some 'special' assembly code
|
# we need more specific compiler hints for some 'special' assembly code
|
||||||
# override -march=armv7-a because it conflicts with -mcpu=cortex-a15
|
# 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 less specific configuration
|
||||||
include $(REP_DIR)/lib/mk/spec/exynos5/core-hw.inc
|
include $(REP_DIR)/lib/mk/spec/exynos5/core-hw.inc
|
||||||
|
|
|
@ -10,7 +10,6 @@ INC_DIR += $(BASE_DIR)/../base-hw/src/core/spec/arm_gic
|
||||||
|
|
||||||
# add C++ sources
|
# add C++ sources
|
||||||
SRC_CC += spec/cortex_a9/kernel/cpu.cc
|
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/board.cc
|
||||||
SRC_CC += spec/cortex_a9/timer.cc
|
SRC_CC += spec/cortex_a9/timer.cc
|
||||||
SRC_CC += spec/arm/smp/kernel/thread_update_pd.cc
|
SRC_CC += spec/arm/smp/kernel/thread_update_pd.cc
|
||||||
|
|
|
@ -9,6 +9,6 @@ SRC_CC += hw/spec/arm/arm_v7_cpu.cc
|
||||||
SRC_CC += hw/spec/32bit/memory_map.cc
|
SRC_CC += hw/spec/32bit/memory_map.cc
|
||||||
SRC_S += bootstrap/spec/arm/crt0.s
|
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
|
include $(BASE_DIR)/../base-hw/lib/mk/bootstrap-hw.inc
|
||||||
|
|
|
@ -13,7 +13,7 @@ SRC_CC += platform_services.cc
|
||||||
|
|
||||||
NR_OF_CPUS += 2
|
NR_OF_CPUS += 2
|
||||||
|
|
||||||
CC_MARCH = -mcpu=cortex-a9
|
CC_MARCH = -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=softfp
|
||||||
|
|
||||||
# include less specific configuration
|
# include less specific configuration
|
||||||
include $(REP_DIR)/lib/mk/spec/cortex_a9/core-hw.inc
|
include $(REP_DIR)/lib/mk/spec/cortex_a9/core-hw.inc
|
||||||
|
|
|
@ -62,6 +62,22 @@
|
||||||
add sp, r0, r1
|
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 **
|
** Jump to high-level entry point **
|
||||||
************************************/
|
************************************/
|
||||||
|
|
|
@ -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);
|
Context(bool privileged);
|
||||||
};
|
};
|
||||||
|
@ -146,8 +152,6 @@ struct Genode::Arm_cpu : public Hw::Arm_cpu
|
||||||
** Dummies **
|
** Dummies **
|
||||||
*************/
|
*************/
|
||||||
|
|
||||||
bool retry_undefined_instr(Context&) { return false; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return kernel name of the executing CPU
|
* Return kernel name of the executing CPU
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -115,6 +115,17 @@
|
||||||
stmia r0!, {r1-r3} /* save pc, cpsr and exception type */
|
stmia r0!, {r1-r3} /* save pc, cpsr and exception type */
|
||||||
clrex /* clear exclusive access needed for cmpxchg */
|
clrex /* clear exclusive access needed for cmpxchg */
|
||||||
cps #SVC_MODE
|
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
|
adr lr, _kernel_entry
|
||||||
ldr lr, [lr]
|
ldr lr, [lr]
|
||||||
bx lr
|
bx lr
|
||||||
|
@ -122,6 +133,9 @@
|
||||||
_kernel_entry:
|
_kernel_entry:
|
||||||
.long kernel
|
.long kernel
|
||||||
|
|
||||||
|
_fpu_save:
|
||||||
|
.long vfp_save_fpu_context
|
||||||
|
|
||||||
|
|
||||||
.section .text
|
.section .text
|
||||||
|
|
||||||
|
@ -133,3 +147,21 @@
|
||||||
idle_thread_main:
|
idle_thread_main:
|
||||||
wfi
|
wfi
|
||||||
b idle_thread_main
|
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
|
||||||
|
|
|
@ -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_ */
|
|
|
@ -19,6 +19,9 @@
|
||||||
|
|
||||||
using namespace Kernel;
|
using namespace Kernel;
|
||||||
|
|
||||||
|
extern "C" void kernel_to_user_context_switch(Cpu::Context*, Cpu::Fpu_context*);
|
||||||
|
|
||||||
|
|
||||||
void Thread::exception(Cpu & cpu)
|
void Thread::exception(Cpu & cpu)
|
||||||
{
|
{
|
||||||
switch (regs->cpu_exception) {
|
switch (regs->cpu_exception) {
|
||||||
|
@ -34,7 +37,6 @@ void Thread::exception(Cpu & cpu)
|
||||||
_interrupt(cpu.id());
|
_interrupt(cpu.id());
|
||||||
return;
|
return;
|
||||||
case Cpu::Context::UNDEFINED_INSTRUCTION:
|
case Cpu::Context::UNDEFINED_INSTRUCTION:
|
||||||
if (_cpu->retry_undefined_instr(*regs)) { return; }
|
|
||||||
Genode::warning(*this, ": undefined instruction at ip=",
|
Genode::warning(*this, ": undefined instruction at ip=",
|
||||||
Genode::Hex(regs->ip));
|
Genode::Hex(regs->ip));
|
||||||
_die();
|
_die();
|
||||||
|
@ -106,14 +108,8 @@ void Thread::proceed(Cpu & cpu)
|
||||||
cpu.switch_to(*regs, pd()->mmu_regs);
|
cpu.switch_to(*regs, pd()->mmu_regs);
|
||||||
|
|
||||||
regs->cpu_exception = cpu.stack_start();
|
regs->cpu_exception = cpu.stack_start();
|
||||||
|
kernel_to_user_context_switch((static_cast<Cpu::Context*>(&*regs)),
|
||||||
asm volatile("mov sp, %0 \n"
|
(static_cast<Cpu::Fpu_context*>(&*regs)));
|
||||||
"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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 }
|
|
@ -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 }
|
|
@ -69,10 +69,14 @@ monitor_mode_exception_vector:
|
||||||
_nonsecure_kernel_entry:
|
_nonsecure_kernel_entry:
|
||||||
ldr lr, [sp, #17*4] /* load kernel sp from vm context */
|
ldr lr, [sp, #17*4] /* load kernel sp from vm context */
|
||||||
stmia r0!, {r1-r2} /* save spsr, and exception reason */
|
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, r3, c6, c0, 0 /* move DFAR to r3 */
|
||||||
mrc p15, 0, r4, c2, c0, 0 /* move TTBR0 to r4 */
|
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, r5, c2, c0, 1 /* move TTBR1 to r5 */
|
||||||
mrc p15, 0, r6, c2, c0, 2 /* move TTBRC to r6 */
|
mrc p15, 0, r6, c2, c0, 2 /* move TTBRC to r6 */
|
||||||
|
vmrs r7, fpscr /* move FPU ctrl to r7 */
|
||||||
mov r1, #0
|
mov r1, #0
|
||||||
mcr p15, 0, r1, c1, c1, 0 /* disable non-secure bit */
|
mcr p15, 0, r1, c1, c1, 0 /* disable non-secure bit */
|
||||||
.irp mode,27,19,23,18,17 /* save mode specific registers */
|
.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 */
|
stmia r0!, {r1,sp,lr} /* store mode-specific sp and lr */
|
||||||
.endr
|
.endr
|
||||||
stmia r0!, {r8-r12} /* save fiq r8-r12 */
|
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 */
|
cps #22 /* switch back to monitor mode */
|
||||||
mov r0, #0b111010011 /* spsr to SVC mode, irqs masked */
|
mov r0, #0b111010011 /* spsr to SVC mode, irqs masked */
|
||||||
msr spsr_cxsf, r0
|
msr spsr_cxsf, r0
|
||||||
|
@ -109,6 +115,12 @@ monitor_mode_enter_normal_world:
|
||||||
msr spsr_cxfs, r2 /* load mode's spsr */
|
msr spsr_cxfs, r2 /* load mode's spsr */
|
||||||
.endr
|
.endr
|
||||||
ldmia r0!, {r8 - r12} /* load fiq r8-r12 */
|
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 */
|
cps #22 /* switch to monitor mode */
|
||||||
ldmia sp, {r0-lr}^ /* load user r0-r12,sp,lr */
|
ldmia sp, {r0-lr}^ /* load user r0-r12,sp,lr */
|
||||||
str lr, [sp, #17*4] /* store kernel sp in vm context */
|
str lr, [sp, #17*4] /* store kernel sp in vm context */
|
||||||
|
|
|
@ -91,11 +91,16 @@ _host_to_vm:
|
||||||
mcr p15, 0, r10, c6, c0, 0 /* write DFAR */
|
mcr p15, 0, r10, c6, c0, 0 /* write DFAR */
|
||||||
mcr p15, 0, r11, c6, c0, 2 /* write IFAR */
|
mcr p15, 0, r11, c6, c0, 2 /* write IFAR */
|
||||||
mcr p15, 0, r12, c13, c0, 1 /* write CIDR */
|
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, r1, c13, c0, 2 /* write TLS1 */
|
||||||
mcr p15, 0, r2, c13, c0, 3 /* write TLS2 */
|
mcr p15, 0, r2, c13, c0, 3 /* write TLS2 */
|
||||||
mcr p15, 0, r3, c13, c0, 4 /* write TLS3 */
|
mcr p15, 0, r3, c13, c0, 4 /* write TLS3 */
|
||||||
mcr p15, 0, r4, c1, c0, 2 /* write CPACR */
|
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 */
|
ldmia sp, {r0-r12} /* load vm's r0-r12 */
|
||||||
eret
|
eret
|
||||||
|
|
||||||
|
@ -106,6 +111,8 @@ _vm_to_host:
|
||||||
mcrr p15, 6, r1, r1, c2 /* write VTTBR */
|
mcrr p15, 6, r1, r1, c2 /* write VTTBR */
|
||||||
mcr p15, 4, r1, c1, c1, 0 /* write HCR register */
|
mcr p15, 4, r1, c1, c1, 0 /* write HCR register */
|
||||||
mcr p15, 4, r1, c1, c1, 3 /* write HSTR 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 */
|
mcr p15, 0, r1, c1, c0, 2 /* write CPACR */
|
||||||
mrs r1, ELR_hyp /* read ip */
|
mrs r1, ELR_hyp /* read ip */
|
||||||
mrs r2, spsr /* read cpsr */
|
mrs r2, spsr /* read cpsr */
|
||||||
|
@ -130,7 +137,15 @@ _vm_to_host:
|
||||||
mrc p15, 0, r10, c13, c0, 2 /* read TLS1 */
|
mrc p15, 0, r10, c13, c0, 2 /* read TLS1 */
|
||||||
mrc p15, 0, r11, c13, c0, 3 /* read TLS2 */
|
mrc p15, 0, r11, c13, c0, 3 /* read TLS2 */
|
||||||
mrc p15, 0, r12, c13, c0, 4 /* read TLS3 */
|
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
|
add r0, sp, #13*4
|
||||||
ldr r3, _vt_host_context_ptr
|
ldr r3, _vt_host_context_ptr
|
||||||
ldr sp, [r3]
|
ldr sp, [r3]
|
||||||
|
|
|
@ -135,13 +135,6 @@ class Genode::Cpu : public Arm_v7_cpu
|
||||||
if (mmu_context.id() && (Ttbr0_64bit::read() != mmu_context.ttbr0))
|
if (mmu_context.id() && (Ttbr0_64bit::read() != mmu_context.ttbr0))
|
||||||
Ttbr0_64bit::write(mmu_context.ttbr0);
|
Ttbr0_64bit::write(mmu_context.ttbr0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*************
|
|
||||||
** Dummies **
|
|
||||||
*************/
|
|
||||||
|
|
||||||
bool retry_undefined_instr(Context&) { return false; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _CORE__SPEC__CORTEX_A15__CPU_H_ */
|
#endif /* _CORE__SPEC__CORTEX_A15__CPU_H_ */
|
||||||
|
|
|
@ -16,75 +16,43 @@
|
||||||
#define _CORE__SPEC__CORTEX_A9__CPU_H_
|
#define _CORE__SPEC__CORTEX_A9__CPU_H_
|
||||||
|
|
||||||
/* core includes */
|
/* core includes */
|
||||||
#include <spec/arm/fpu.h>
|
|
||||||
#include <spec/arm_v7/cpu_support.h>
|
#include <spec/arm_v7/cpu_support.h>
|
||||||
#include <board.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:
|
/**
|
||||||
|
* Write back dirty cache lines and invalidate whole data cache
|
||||||
|
*/
|
||||||
|
void clean_invalidate_data_cache()
|
||||||
|
{
|
||||||
|
clean_invalidate_inner_data_cache();
|
||||||
|
Board::l2_cache().clean_invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
Fpu _fpu { };
|
/**
|
||||||
|
* Invalidate whole data cache
|
||||||
|
*/
|
||||||
|
void invalidate_data_cache()
|
||||||
|
{
|
||||||
|
invalidate_inner_data_cache();
|
||||||
|
Board::l2_cache().invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
/**
|
||||||
|
* Clean and invalidate data-cache for virtual region
|
||||||
|
* 'base' - 'base + size'
|
||||||
|
*/
|
||||||
|
void clean_invalidate_data_cache_by_virt_region(addr_t base,
|
||||||
|
size_t const size)
|
||||||
|
{
|
||||||
|
Arm_cpu::clean_invalidate_data_cache_by_virt_region(base, size);
|
||||||
|
Board::l2_cache().clean_invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
struct Context : Arm_cpu::Context, Fpu::Context
|
static unsigned executing_id() { return Mpidr::Aff_0::get(Mpidr::read()); }
|
||||||
{
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
void clean_invalidate_data_cache()
|
|
||||||
{
|
|
||||||
clean_invalidate_inner_data_cache();
|
|
||||||
Board::l2_cache().clean_invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidate whole data cache
|
|
||||||
*/
|
|
||||||
void invalidate_data_cache()
|
|
||||||
{
|
|
||||||
invalidate_inner_data_cache();
|
|
||||||
Board::l2_cache().invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean and invalidate data-cache for virtual region
|
|
||||||
* 'base' - 'base + size'
|
|
||||||
*/
|
|
||||||
void clean_invalidate_data_cache_by_virt_region(addr_t base,
|
|
||||||
size_t const size)
|
|
||||||
{
|
|
||||||
Arm_cpu::clean_invalidate_data_cache_by_virt_region(base, size);
|
|
||||||
Board::l2_cache().clean_invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned executing_id() { return Mpidr::Aff_0::get(Mpidr::read()); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _CORE__SPEC__CORTEX_A9__CPU_H_ */
|
#endif /* _CORE__SPEC__CORTEX_A9__CPU_H_ */
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -23,8 +23,6 @@
|
||||||
|
|
||||||
void Kernel::Cpu::init(Kernel::Pic &pic)
|
void Kernel::Cpu::init(Kernel::Pic &pic)
|
||||||
{
|
{
|
||||||
_fpu.init();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Lock::Guard guard(data_lock());
|
Lock::Guard guard(data_lock());
|
||||||
|
|
||||||
|
|
|
@ -116,11 +116,6 @@ class Genode::Cpu : public Hw::X86_64_cpu
|
||||||
|
|
||||||
Fpu & fpu() { return _fpu; }
|
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
|
* Return kernel name of the executing CPU
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
SPECS += arm
|
SPECS += arm
|
||||||
REP_INC_DIR += include/spec/arm_v6
|
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
|
include $(BASE_DIR)/mk/spec/arm.mk
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
SPECS += arm_v7
|
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
|
include $(BASE_DIR)/mk/spec/arm_v7.mk
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Test pseudo-parallel use of FPU if available
|
|
|
@ -1,2 +0,0 @@
|
||||||
_/src/init
|
|
||||||
_/src/test-fpu
|
|
|
@ -1 +0,0 @@
|
||||||
2018-11-01 f8565783b90caaa5724a58e6ed7be9c999211fe3
|
|
|
@ -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>
|
|
|
@ -1,2 +0,0 @@
|
||||||
SRC_DIR = src/test/fpu
|
|
||||||
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
|
|
|
@ -1 +0,0 @@
|
||||||
2018-11-01 fc82355e25add8ece1cfe7a4c93b8c42e4866049
|
|
|
@ -1 +0,0 @@
|
||||||
base
|
|
|
@ -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); }
|
|
|
@ -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
|
|
|
@ -44,7 +44,6 @@ set avail_test_pkgs {
|
||||||
test-dynamic_config_slave
|
test-dynamic_config_slave
|
||||||
test-expat
|
test-expat
|
||||||
test-fault_detection
|
test-fault_detection
|
||||||
test-fpu
|
|
||||||
test-fs_log
|
test-fs_log
|
||||||
test-fs_packet
|
test-fs_packet
|
||||||
test-fs_report
|
test-fs_report
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
TARGET = test-ieee754
|
||||||
|
LIBS = libc libm
|
||||||
|
SRC_C = tst-ieee754.c
|
||||||
|
SRC_CC = component.cc
|
|
@ -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;
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ fetchurl_lxip
|
||||||
fetchurl_lwip
|
fetchurl_lwip
|
||||||
fs_query
|
fs_query
|
||||||
gdb_monitor
|
gdb_monitor
|
||||||
|
ieee754
|
||||||
init_smp
|
init_smp
|
||||||
input_filter
|
input_filter
|
||||||
libc_fatfs
|
libc_fatfs
|
||||||
|
|
Loading…
Reference in New Issue