8302976: C2 intrinsification of Float.floatToFloat16 and Float.float16ToFloat yields different result than the interpreter

Reviewed-by: sviswanathan, jbhateja, vlivanov
This commit is contained in:
Vladimir Kozlov 2023-03-09 03:26:38 +00:00
parent 02875e77fd
commit 8cfd74f76a
47 changed files with 1254 additions and 195 deletions

View File

@ -15043,8 +15043,7 @@ instruct convF2HF_reg_reg(iRegINoSp dst, vRegF src, vRegF tmp) %{
%}
effect(TEMP tmp);
ins_encode %{
__ fcvtsh($tmp$$FloatRegister, $src$$FloatRegister);
__ smov($dst$$Register, $tmp$$FloatRegister, __ H, 0);
__ flt_to_flt16($dst$$Register, $src$$FloatRegister, $tmp$$FloatRegister);
%}
ins_pipe(pipe_slow);
%}
@ -15056,8 +15055,7 @@ instruct convHF2F_reg_reg(vRegF dst, iRegINoSp src, vRegF tmp) %{
%}
effect(TEMP tmp);
ins_encode %{
__ mov($tmp$$FloatRegister, __ H, 0, $src$$Register);
__ fcvths($dst$$FloatRegister, $tmp$$FloatRegister);
__ flt16_to_flt($dst$$FloatRegister, $src$$Register, $tmp$$FloatRegister);
%}
ins_pipe(pipe_slow);
%}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -1814,10 +1814,12 @@ void LIR_Assembler::arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr
void LIR_Assembler::arith_fpu_implementation(LIR_Code code, int left_index, int right_index, int dest_index, bool pop_fpu_stack) { Unimplemented(); }
void LIR_Assembler::intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr unused, LIR_Opr dest, LIR_Op* op) {
void LIR_Assembler::intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr tmp, LIR_Opr dest, LIR_Op* op) {
switch(code) {
case lir_abs : __ fabsd(dest->as_double_reg(), value->as_double_reg()); break;
case lir_sqrt: __ fsqrtd(dest->as_double_reg(), value->as_double_reg()); break;
case lir_f2hf: __ flt_to_flt16(dest->as_register(), value->as_float_reg(), tmp->as_float_reg()); break;
case lir_hf2f: __ flt16_to_flt(dest->as_float_reg(), value->as_register(), tmp->as_float_reg()); break;
default : ShouldNotReachHere();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -752,20 +752,35 @@ void LIRGenerator::do_MathIntrinsic(Intrinsic* x) {
switch (x->id()) {
case vmIntrinsics::_dabs:
case vmIntrinsics::_dsqrt:
case vmIntrinsics::_dsqrt_strict: {
case vmIntrinsics::_dsqrt_strict:
case vmIntrinsics::_floatToFloat16:
case vmIntrinsics::_float16ToFloat: {
assert(x->number_of_arguments() == 1, "wrong type");
LIRItem value(x->argument_at(0), this);
value.load_item();
LIR_Opr src = value.result();
LIR_Opr dst = rlock_result(x);
switch (x->id()) {
case vmIntrinsics::_dsqrt:
case vmIntrinsics::_dsqrt_strict: {
__ sqrt(value.result(), dst, LIR_OprFact::illegalOpr);
__ sqrt(src, dst, LIR_OprFact::illegalOpr);
break;
}
case vmIntrinsics::_dabs: {
__ abs(value.result(), dst, LIR_OprFact::illegalOpr);
__ abs(src, dst, LIR_OprFact::illegalOpr);
break;
}
case vmIntrinsics::_floatToFloat16: {
LIR_Opr tmp = new_register(T_FLOAT);
__ move(LIR_OprFact::floatConst(-0.0), tmp);
__ f2hf(src, dst, tmp);
break;
}
case vmIntrinsics::_float16ToFloat: {
LIR_Opr tmp = new_register(T_FLOAT);
__ move(LIR_OprFact::floatConst(-0.0), tmp);
__ hf2f(src, dst, tmp);
break;
}
default:

View File

@ -513,8 +513,15 @@ public:
orr(Vd, T, Vn, Vn);
}
void flt_to_flt16(Register dst, FloatRegister src, FloatRegister tmp) {
fcvtsh(tmp, src);
smov(dst, tmp, H, 0);
}
public:
void flt16_to_flt(FloatRegister dst, Register src, FloatRegister tmp) {
mov(tmp, H, 0, src);
fcvths(dst, tmp);
}
// Generalized Test Bit And Branch, including a "far" variety which
// spans more than 32KiB.

View File

@ -300,6 +300,50 @@ void TemplateInterpreterGenerator::generate_transcendental_entry(AbstractInterpr
__ blr(rscratch1);
}
address TemplateInterpreterGenerator::generate_Float_float16ToFloat_entry() {
// vmIntrinsics checks InlineIntrinsics flag, no need to check it here.
if (!VM_Version::supports_float16() ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_float16ToFloat) ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_floatToFloat16)) {
return nullptr;
}
// r19_sender_sp: sender sp
// stack:
// [ arg ] <-- esp
// [ arg ]
// retaddr in lr
// result in v0
address entry_point = __ pc();
__ ldrw(c_rarg0, Address(esp));
__ flt16_to_flt(v0, c_rarg0, v1);
__ mov(sp, r19_sender_sp); // Restore caller's SP
__ br(lr);
return entry_point;
}
address TemplateInterpreterGenerator::generate_Float_floatToFloat16_entry() {
// vmIntrinsics checks InlineIntrinsics flag, no need to check it here.
if (!VM_Version::supports_float16() ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_float16ToFloat) ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_floatToFloat16)) {
return nullptr;
}
// r19_sender_sp: sender sp
// stack:
// [ arg ] <-- esp
// [ arg ]
// retaddr in lr
// result in c_rarg0
address entry_point = __ pc();
__ ldrs(v0, Address(esp));
__ flt_to_flt16(c_rarg0, v0, v1);
__ mov(sp, r19_sender_sp); // Restore caller's SP
__ br(lr);
return entry_point;
}
// Abstract method entry
// Attempt to execute abstract method. Throw exception
address TemplateInterpreterGenerator::generate_abstract_entry(void) {
@ -1698,6 +1742,7 @@ address TemplateInterpreterGenerator::generate_currentThread() {
return entry_point;
}
//-----------------------------------------------------------------------------
// Exceptions

View File

@ -170,6 +170,8 @@ enum Ampere_CPU_Model {
static bool supports_on_spin_wait() { return _spin_wait.inst() != SpinWait::NONE; }
static bool supports_float16() { return true; }
#ifdef __APPLE__
// Is the CPU running emulated (for example macOS Rosetta running x86_64 code on M1 ARM (aarch64)
static bool is_cpu_emulated();

View File

@ -783,6 +783,8 @@ address TemplateInterpreterGenerator::generate_Reference_get_entry(void) {
address TemplateInterpreterGenerator::generate_CRC32_update_entry() { return nullptr; }
address TemplateInterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind) { return nullptr; }
address TemplateInterpreterGenerator::generate_CRC32C_updateBytes_entry(AbstractInterpreter::MethodKind kind) { return nullptr; }
address TemplateInterpreterGenerator::generate_Float_float16ToFloat_entry() { return nullptr; }
address TemplateInterpreterGenerator::generate_Float_floatToFloat16_entry() { return nullptr; }
//
// Interpreter stub for calling a native method. (asm interpreter)

View File

@ -1933,6 +1933,10 @@ address TemplateInterpreterGenerator::generate_CRC32C_updateBytes_entry(Abstract
return NULL;
}
// Not supported
address TemplateInterpreterGenerator::generate_Float_float16ToFloat_entry() { return nullptr; }
address TemplateInterpreterGenerator::generate_Float_floatToFloat16_entry() { return nullptr; }
// =============================================================================
// Exceptions

View File

@ -798,8 +798,6 @@ enum operand_size { int8, int16, int32, uint32, int64 };
INSN(fsqrt_d, 0b1010011, 0b00000, 0b0101101);
INSN(fcvt_s_d, 0b1010011, 0b00001, 0b0100000);
INSN(fcvt_d_s, 0b1010011, 0b00000, 0b0100001);
INSN(fcvt_s_h, 0b1010011, 0b00010, 0b0100000);
INSN(fcvt_h_s, 0b1010011, 0b00000, 0b0100010);
#undef INSN
// Immediate Instruction
@ -1056,7 +1054,6 @@ enum operand_size { int8, int16, int32, uint32, int64 };
INSN(fmv_w_x, 0b1010011, 0b000, 0b00000, 0b1111000);
INSN(fmv_d_x, 0b1010011, 0b000, 0b00000, 0b1111001);
INSN(fmv_h_x, 0b1010011, 0b000, 0b00000, 0b1111010);
#undef INSN
@ -1077,7 +1074,6 @@ enum operand_size { int8, int16, int32, uint32, int64 };
INSN(fclass_d, 0b1010011, 0b001, 0b00000, 0b1110001);
INSN(fmv_x_w, 0b1010011, 0b000, 0b00000, 0b1110000);
INSN(fmv_x_d, 0b1010011, 0b000, 0b00000, 0b1110001);
INSN(fmv_x_h, 0b1010011, 0b000, 0b00000, 0b1110010);
#undef INSN

View File

@ -103,7 +103,6 @@ define_pd_global(intx, InlineSmallCode, 1000);
product(bool, UseZba, false, EXPERIMENTAL, "Use Zba instructions") \
product(bool, UseZbb, false, EXPERIMENTAL, "Use Zbb instructions") \
product(bool, UseZbs, false, EXPERIMENTAL, "Use Zbs instructions") \
product(bool, UseZfhmin, false, EXPERIMENTAL, "Use Zfhmin instructions") \
product(bool, UseZic64b, false, EXPERIMENTAL, "Use Zic64b instructions") \
product(bool, UseZicbom, false, EXPERIMENTAL, "Use Zicbom instructions") \
product(bool, UseZicbop, false, EXPERIMENTAL, "Use Zicbop instructions") \

View File

@ -1845,10 +1845,6 @@ const bool Matcher::match_rule_supported(int opcode) {
case Op_CountTrailingZerosI:
case Op_CountTrailingZerosL:
return UseZbb;
case Op_ConvF2HF:
case Op_ConvHF2F:
return UseZfhmin;
}
return true; // Per default match rules are supported.
@ -8180,44 +8176,6 @@ instruct convL2F_reg_reg(fRegF dst, iRegL src) %{
ins_pipe(fp_l2f);
%}
// float <-> half float
instruct convHF2F_reg_reg(fRegF dst, iRegINoSp src, fRegF tmp) %{
predicate(UseZfhmin);
match(Set dst (ConvHF2F src));
effect(TEMP tmp);
ins_cost(XFER_COST);
format %{ "fmv.h.x $tmp, $src\t#@convHF2F_reg_reg\n\t"
"fcvt.s.h $dst, $tmp\t#@convHF2F_reg_reg"
%}
ins_encode %{
__ fmv_h_x($tmp$$FloatRegister, $src$$Register);
__ fcvt_s_h($dst$$FloatRegister, $tmp$$FloatRegister);
%}
ins_pipe(fp_i2f);
%}
instruct convF2HF_reg_reg(iRegINoSp dst, fRegF src, fRegF tmp) %{
predicate(UseZfhmin);
match(Set dst (ConvF2HF src));
effect(TEMP tmp);
ins_cost(XFER_COST);
format %{ "fcvt.h.s $tmp, $src\t#@convF2HF_reg_reg\n\t"
"fmv.x.h $dst, $tmp\t#@convF2HF_reg_reg"
%}
ins_encode %{
__ fcvt_h_s($tmp$$FloatRegister, $src$$FloatRegister);
__ fmv_x_h($dst$$Register, $tmp$$FloatRegister);
%}
ins_pipe(fp_f2i);
%}
// double <-> int
instruct convD2I_reg_reg(iRegINoSp dst, fRegD src) %{

View File

@ -301,6 +301,10 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M
return entry_point;
}
// Not supported
address TemplateInterpreterGenerator::generate_Float_float16ToFloat_entry() { return nullptr; }
address TemplateInterpreterGenerator::generate_Float_floatToFloat16_entry() { return nullptr; }
// Abstract method entry
// Attempt to execute abstract method. Throw exception
address TemplateInterpreterGenerator::generate_abstract_entry(void) {

View File

@ -76,9 +76,6 @@ void VM_Version::initialize() {
if (FLAG_IS_DEFAULT(UseZicboz)) {
FLAG_SET_DEFAULT(UseZicboz, true);
}
if (FLAG_IS_DEFAULT(UseZfhmin)) {
FLAG_SET_DEFAULT(UseZfhmin, true);
}
if (FLAG_IS_DEFAULT(UseZihintpause)) {
FLAG_SET_DEFAULT(UseZihintpause, true);
}

View File

@ -2003,6 +2003,10 @@ address TemplateInterpreterGenerator::generate_CRC32C_updateBytes_entry(Abstract
return NULL;
}
// Not supported
address TemplateInterpreterGenerator::generate_Float_float16ToFloat_entry() { return nullptr; }
address TemplateInterpreterGenerator::generate_Float_floatToFloat16_entry() { return nullptr; }
void TemplateInterpreterGenerator::bang_stack_shadow_pages(bool native_call) {
// Quick & dirty stack overflow checking: bang the stack & handle trap.
// Note that we do the banging after the frame is setup, since the exception

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -2454,6 +2454,10 @@ void LIR_Assembler::intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr tmp, LIR_
default : ShouldNotReachHere();
}
#endif // !_LP64
} else if (code == lir_f2hf) {
__ flt_to_flt16(dest->as_register(), value->as_xmm_float_reg(), tmp->as_xmm_float_reg());
} else if (code == lir_hf2f) {
__ flt16_to_flt(dest->as_xmm_float_reg(), value->as_register());
} else {
Unimplemented();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -832,6 +832,10 @@ void LIRGenerator::do_MathIntrinsic(Intrinsic* x) {
__ move(LIR_OprFact::doubleConst(-0.0), tmp);
}
#endif
if (x->id() == vmIntrinsics::_floatToFloat16) {
tmp = new_register(T_FLOAT);
__ move(LIR_OprFact::floatConst(-0.0), tmp);
}
switch(x->id()) {
case vmIntrinsics::_dabs:
@ -841,6 +845,12 @@ void LIRGenerator::do_MathIntrinsic(Intrinsic* x) {
case vmIntrinsics::_dsqrt_strict:
__ sqrt(calc_input, calc_result, LIR_OprFact::illegalOpr);
break;
case vmIntrinsics::_floatToFloat16:
__ f2hf(calc_input, calc_result, tmp);
break;
case vmIntrinsics::_float16ToFloat:
__ hf2f(calc_input, calc_result, LIR_OprFact::illegalOpr);
break;
default:
ShouldNotReachHere();
}

View File

@ -162,6 +162,11 @@ class MacroAssembler: public Assembler {
void incrementq(Register reg, int value = 1);
void incrementq(Address dst, int value = 1);
void incrementl(AddressLiteral dst, Register rscratch = noreg);
void incrementl(ArrayAddress dst, Register rscratch);
void incrementq(AddressLiteral dst, Register rscratch = noreg);
// Support optimal SSE move instructions.
void movflt(XMMRegister dst, XMMRegister src) {
if (dst-> encoding() == src->encoding()) return;
@ -189,10 +194,18 @@ class MacroAssembler: public Assembler {
}
void movdbl(Address dst, XMMRegister src) { movsd(dst, src); }
void incrementl(AddressLiteral dst, Register rscratch = noreg);
void incrementl(ArrayAddress dst, Register rscratch);
void flt_to_flt16(Register dst, XMMRegister src, XMMRegister tmp) {
// Use separate tmp XMM register because caller may
// requires src XMM register to be unchanged (as in x86.ad).
vcvtps2ph(tmp, src, 0x04, Assembler::AVX_128bit);
movdl(dst, tmp);
movswl(dst, dst);
}
void incrementq(AddressLiteral dst, Register rscratch = noreg);
void flt16_to_flt(XMMRegister dst, Register src) {
movdl(dst, src);
vcvtph2ps(dst, dst, Assembler::AVX_128bit);
}
// Alignment
void align32();

View File

@ -3518,6 +3518,55 @@ void StubGenerator::generate_libm_stubs() {
}
}
/**
* Arguments:
*
* Input:
* c_rarg0 - float16 jshort
*
* Output:
* xmm0 - float
*/
address StubGenerator::generate_float16ToFloat() {
StubCodeMark mark(this, "StubRoutines", "float16ToFloat");
address start = __ pc();
BLOCK_COMMENT("Entry:");
// No need for RuntimeStub frame since it is called only during JIT compilation
// Load value into xmm0 and convert
__ flt16_to_flt(xmm0, c_rarg0);
__ ret(0);
return start;
}
/**
* Arguments:
*
* Input:
* xmm0 - float
*
* Output:
* rax - float16 jshort
*/
address StubGenerator::generate_floatToFloat16() {
StubCodeMark mark(this, "StubRoutines", "floatToFloat16");
address start = __ pc();
BLOCK_COMMENT("Entry:");
// No need for RuntimeStub frame since it is called only during JIT compilation
// Convert and put result into rax
__ flt_to_flt16(rax, xmm0, xmm1);
__ ret(0);
return start;
}
address StubGenerator::generate_cont_thaw(const char* label, Continuation::thaw_kind kind) {
if (!Continuations::enabled()) return nullptr;
@ -3883,6 +3932,16 @@ void StubGenerator::generate_initial() {
StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32();
}
if (VM_Version::supports_float16()) {
// For results consistency both intrinsics should be enabled.
// vmIntrinsics checks InlineIntrinsics flag, no need to check it here.
if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_float16ToFloat) &&
vmIntrinsics::is_intrinsic_available(vmIntrinsics::_floatToFloat16)) {
StubRoutines::_hf2f = generate_float16ToFloat();
StubRoutines::_f2hf = generate_floatToFloat16();
}
}
generate_libm_stubs();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -474,6 +474,8 @@ class StubGenerator: public StubCodeGenerator {
address generate_bigIntegerRightShift();
address generate_bigIntegerLeftShift();
address generate_float16ToFloat();
address generate_floatToFloat16();
// Libm trigonometric stubs

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -314,6 +314,58 @@ address TemplateInterpreterGenerator::generate_Double_doubleToRawLongBits_entry(
return NULL;
}
/**
* Method entry for static method:
* java.lang.Float.float16ToFloat(short floatBinary16)
*/
address TemplateInterpreterGenerator::generate_Float_float16ToFloat_entry() {
// vmIntrinsics checks InlineIntrinsics flag, no need to check it here.
if (!VM_Version::supports_float16() ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_float16ToFloat) ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_floatToFloat16)) {
return nullptr; // Generate a vanilla entry
}
address entry = __ pc();
// rsi: the sender's SP
// Load value into xmm0 and convert
__ movswl(rax, Address(rsp, wordSize));
__ flt16_to_flt(xmm0, rax);
// Return
__ pop(rdi); // get return address
__ mov(rsp, rsi); // set rsp to the sender's SP
__ jmp(rdi);
return entry;
}
/**
* Method entry for static method:
* java.lang.Float.floatToFloat16(float value)
*/
address TemplateInterpreterGenerator::generate_Float_floatToFloat16_entry() {
// vmIntrinsics checks InlineIntrinsics flag, no need to check it here.
if (!VM_Version::supports_float16() ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_floatToFloat16) ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_float16ToFloat)) {
return nullptr; // Generate a vanilla entry
}
address entry = __ pc();
// rsi: the sender's SP
// Load value into xmm0, convert and put result into rax
__ movflt(xmm0, Address(rsp, wordSize));
__ flt_to_flt16(rax, xmm0, xmm1);
// Return
__ pop(rdi); // get return address
__ mov(rsp, rsi); // set rsp to the sender's SP
__ jmp(rdi);
return entry;
}
address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::MethodKind kind) {
// rbx,: Method*

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -332,6 +332,60 @@ address TemplateInterpreterGenerator::generate_CRC32C_updateBytes_entry(Abstract
return NULL;
}
/**
* Method entry for static method:
* java.lang.Float.float16ToFloat(short floatBinary16)
*/
address TemplateInterpreterGenerator::generate_Float_float16ToFloat_entry() {
// vmIntrinsics checks InlineIntrinsics flag, no need to check it here.
if (!VM_Version::supports_float16() ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_float16ToFloat) ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_floatToFloat16)) {
return nullptr; // Generate a vanilla entry
}
address entry = __ pc();
// r13: the sender's SP
// Load value into xmm0 and convert
__ movswl(rax, Address(rsp, wordSize));
__ flt16_to_flt(xmm0, rax);
// Return result in xmm0
__ pop(rdi); // get return address
__ mov(rsp, r13); // set rsp to sender's SP
__ jmp(rdi);
return entry;
}
/**
* Method entry for static method:
* java.lang.Float.floatToFloat16(float value)
*/
address TemplateInterpreterGenerator::generate_Float_floatToFloat16_entry() {
// vmIntrinsics checks InlineIntrinsics flag, no need to check it here.
if (!VM_Version::supports_float16() ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_floatToFloat16) ||
vmIntrinsics::is_disabled_by_flags(vmIntrinsics::_float16ToFloat)) {
return nullptr; // Generate a vanilla entry
}
address entry = __ pc();
// r13: the sender's SP
// Load value into xmm0, convert and put result into rax
__ movflt(xmm0, Address(rsp, wordSize));
__ flt_to_flt16(rax, xmm0, xmm1);
// Return result in rax
__ pop(rdi); // get return address
__ mov(rsp, r13); // set rsp to sender's SP
__ jmp(rdi);
return entry;
}
//
// Various method entries
//

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -759,6 +759,11 @@ public:
return true;
}
// For AVX CPUs only. f16c support is disabled if UseAVX == 0.
static bool supports_float16() {
return supports_f16c() || supports_avx512vl();
}
// there are several insns to force cache line sync to memory which
// we can use to ensure mapped non-volatile memory is up to date with
// pending in-cache changes.

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
// Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
// This code is free software; you can redistribute it and/or modify it
@ -1683,7 +1683,7 @@ const bool Matcher::match_rule_supported(int opcode) {
break;
case Op_ConvF2HF:
case Op_ConvHF2F:
if (!VM_Version::supports_f16c() && !VM_Version::supports_avx512vl()) {
if (!VM_Version::supports_float16()) {
return false;
}
break;
@ -3665,9 +3665,7 @@ instruct convF2HF_reg_reg(rRegI dst, regF src, regF tmp) %{
ins_cost(125);
format %{ "vcvtps2ph $dst,$src \t using $tmp as TEMP"%}
ins_encode %{
__ vcvtps2ph($tmp$$XMMRegister, $src$$XMMRegister, 0x04, Assembler::AVX_128bit);
__ movdl($dst$$Register, $tmp$$XMMRegister);
__ movswl($dst$$Register, $dst$$Register);
__ flt_to_flt16($dst$$Register, $src$$XMMRegister, $tmp$$XMMRegister);
%}
ins_pipe( pipe_slow );
%}
@ -3709,8 +3707,7 @@ instruct convHF2F_reg_reg(regF dst, rRegI src) %{
match(Set dst (ConvHF2F src));
format %{ "vcvtph2ps $dst,$src" %}
ins_encode %{
__ movdl($dst$$XMMRegister, $src$$Register);
__ vcvtph2ps($dst$$XMMRegister, $dst$$XMMRegister, Assembler::AVX_128bit);
__ flt16_to_flt($dst$$XMMRegister, $src$$Register);
%}
ins_pipe( pipe_slow );
%}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -135,6 +135,10 @@ bool Compiler::is_intrinsic_supported(const methodHandle& method) {
case vmIntrinsics::_onSpinWait:
if (!VM_Version::supports_on_spin_wait()) return false;
break;
case vmIntrinsics::_floatToFloat16:
case vmIntrinsics::_float16ToFloat:
if (!VM_Version::supports_float16()) return false;
break;
case vmIntrinsics::_arraycopy:
case vmIntrinsics::_currentTimeMillis:
case vmIntrinsics::_nanoTime:

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -563,6 +563,8 @@ void LIR_OpVisitState::visit(LIR_Op* op) {
case lir_sqrt:
case lir_abs:
case lir_neg:
case lir_f2hf:
case lir_hf2f:
case lir_logic_and:
case lir_logic_or:
case lir_logic_xor:
@ -1731,6 +1733,8 @@ const char * LIR_Op::name() const {
case lir_abs: s = "abs"; break;
case lir_neg: s = "neg"; break;
case lir_sqrt: s = "sqrt"; break;
case lir_f2hf: s = "f2hf"; break;
case lir_hf2f: s = "hf2f"; break;
case lir_logic_and: s = "logic_and"; break;
case lir_logic_or: s = "logic_or"; break;
case lir_logic_xor: s = "logic_xor"; break;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -958,6 +958,8 @@ enum LIR_Code {
, lir_abs
, lir_neg
, lir_tan
, lir_f2hf
, lir_hf2f
, lir_log10
, lir_logic_and
, lir_logic_or
@ -2272,6 +2274,8 @@ class LIR_List: public CompilationResourceObj {
void fmaf(LIR_Opr from, LIR_Opr from1, LIR_Opr from2, LIR_Opr to) { append(new LIR_Op3(lir_fmaf, from, from1, from2, to)); }
void log10 (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_log10, from, LIR_OprFact::illegalOpr, to, tmp)); }
void tan (LIR_Opr from, LIR_Opr to, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_Op2(lir_tan , from, tmp1, to, tmp2)); }
void f2hf(LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_f2hf, from, tmp, to)); }
void hf2f(LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_hf2f, from, tmp, to)); }
void add (LIR_Opr left, LIR_Opr right, LIR_Opr res) { append(new LIR_Op2(lir_add, left, right, res)); }
void sub (LIR_Opr left, LIR_Opr right, LIR_Opr res, CodeEmitInfo* info = NULL) { append(new LIR_Op2(lir_sub, left, right, res, info)); }

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -726,6 +726,8 @@ void LIR_Assembler::emit_op2(LIR_Op2* op) {
case lir_sqrt:
case lir_tan:
case lir_log10:
case lir_f2hf:
case lir_hf2f:
intrinsic_op(op->code(), op->in_opr1(), op->in_opr2(), op->result_opr(), op);
break;

View File

@ -2965,6 +2965,10 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) {
case vmIntrinsics::_fmaD: do_FmaIntrinsic(x); break;
case vmIntrinsics::_fmaF: do_FmaIntrinsic(x); break;
// Use java.lang.Math intrinsics code since it works for these intrinsics too.
case vmIntrinsics::_floatToFloat16: // fall through
case vmIntrinsics::_float16ToFloat: do_MathIntrinsic(x); break;
case vmIntrinsics::_Preconditions_checkIndex:
do_PreconditionsCheckIndex(x, T_INT);
break;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -6731,6 +6731,8 @@ void LinearScanStatistic::collect(LinearScan* allocator) {
case lir_rem:
case lir_sqrt:
case lir_abs:
case lir_f2hf:
case lir_hf2f:
case lir_log10:
case lir_logic_and:
case lir_logic_or:

View File

@ -309,6 +309,10 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_fmaF:
if (!InlineMathNatives || !UseFMA) return true;
break;
case vmIntrinsics::_floatToFloat16:
case vmIntrinsics::_float16ToFloat:
if (!InlineIntrinsics) return true;
break;
case vmIntrinsics::_arraycopy:
if (!InlineArrayCopy) return true;
break;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -125,7 +125,7 @@ class AbstractCompiler : public CHeapObj<mtCompiler> {
// GraphBuilder::GraphBuilder() in src/share/vm/c1/c1_GraphBuilder.cpp
// for more details.
virtual bool is_intrinsic_available(const methodHandle& method, DirectiveSet* directive) {
bool is_intrinsic_available(const methodHandle& method, DirectiveSet* directive) {
return is_intrinsic_supported(method) &&
!directive->is_intrinsic_disabled(method) &&
!vmIntrinsics::is_disabled_by_flags(method);

View File

@ -133,6 +133,8 @@ AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(const methodHan
case vmIntrinsics::_floatToRawIntBits: return java_lang_Float_floatToRawIntBits;
case vmIntrinsics::_longBitsToDouble: return java_lang_Double_longBitsToDouble;
case vmIntrinsics::_doubleToRawLongBits: return java_lang_Double_doubleToRawLongBits;
case vmIntrinsics::_float16ToFloat: return java_lang_Float_float16ToFloat;
case vmIntrinsics::_floatToFloat16: return java_lang_Float_floatToFloat16;
#if defined(AMD64) || defined(AARCH64) || defined(RISCV64)
case vmIntrinsics::_currentThread: return java_lang_Thread_currentThread;
#endif

View File

@ -88,6 +88,8 @@ class AbstractInterpreter: AllStatic {
java_util_zip_CRC32C_updateDirectByteBuffer, // implementation of java.util.zip.CRC32C.updateDirectByteBuffer(crc, address, off, end)
java_lang_Float_intBitsToFloat, // implementation of java.lang.Float.intBitsToFloat()
java_lang_Float_floatToRawIntBits, // implementation of java.lang.Float.floatToRawIntBits()
java_lang_Float_float16ToFloat, // implementation of java.lang.Float.float16ToFloat()
java_lang_Float_floatToFloat16, // implementation of java.lang.Float.floatToFloat16()
java_lang_Double_longBitsToDouble, // implementation of java.lang.Double.longBitsToDouble()
java_lang_Double_doubleToRawLongBits, // implementation of java.lang.Double.doubleToRawLongBits()
java_lang_Thread_currentThread, // implementation of java.lang.Thread.currentThread()
@ -157,6 +159,8 @@ class AbstractInterpreter: AllStatic {
case vmIntrinsics::_dexp : // fall thru
case vmIntrinsics::_fmaD : // fall thru
case vmIntrinsics::_fmaF : // fall thru
case vmIntrinsics::_floatToFloat16 : // fall thru
case vmIntrinsics::_float16ToFloat : // fall thru
case vmIntrinsics::_Continuation_doYield : // fall thru
return false;

View File

@ -223,6 +223,9 @@ void TemplateInterpreterGenerator::generate_all() {
method_entry(java_lang_Double_longBitsToDouble);
method_entry(java_lang_Double_doubleToRawLongBits);
method_entry(java_lang_Float_float16ToFloat);
method_entry(java_lang_Float_floatToFloat16);
#undef method_entry
// Bytecodes
@ -437,7 +440,10 @@ address TemplateInterpreterGenerator::generate_method_entry(
case Interpreter::java_lang_Thread_currentThread
: entry_point = generate_currentThread(); break;
#endif
case Interpreter::java_lang_Float_float16ToFloat
: entry_point = generate_Float_float16ToFloat_entry(); break;
case Interpreter::java_lang_Float_floatToFloat16
: entry_point = generate_Float_floatToFloat16_entry(); break;
#ifdef IA32
// On x86_32 platforms, a special entry is generated for the following four methods.
// On other platforms the normal entry is used to enter these methods.

View File

@ -103,6 +103,9 @@ class TemplateInterpreterGenerator: public AbstractInterpreterGenerator {
address generate_Double_longBitsToDouble_entry();
address generate_Double_doubleToRawLongBits_entry();
#endif // IA32
address generate_Float_float16ToFloat_entry();
address generate_Float_floatToFloat16_entry();
// Some platforms don't need registers, other need two. Unused function is
// left unimplemented.
void generate_stack_overflow_check(void);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -29,7 +29,7 @@
#include "opto/matcher.hpp"
#include "opto/phaseX.hpp"
#include "opto/subnode.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
//=============================================================================
//------------------------------Identity---------------------------------------
@ -165,15 +165,12 @@ const Type* ConvF2DNode::Value(PhaseGVN* phase) const {
//------------------------------Value------------------------------------------
const Type* ConvF2HFNode::Value(PhaseGVN* phase) const {
const Type *t = phase->type( in(1) );
if( t == Type::TOP ) return Type::TOP;
if( t == Type::FLOAT ) return TypeInt::SHORT;
const TypeF *tf = t->is_float_constant();
return TypeInt::make( SharedRuntime::f2hf( tf->getf() ) );
}
if (t == Type::TOP) return Type::TOP;
if (t == Type::FLOAT) return TypeInt::SHORT;
if (StubRoutines::f2hf_adr() == nullptr) return bottom_type();
//------------------------------Identity---------------------------------------
Node* ConvF2HFNode::Identity(PhaseGVN* phase) {
return (in(1)->Opcode() == Op_ConvHF2F) ? in(1)->in(1) : this;
const TypeF *tf = t->is_float_constant();
return TypeInt::make( StubRoutines::f2hf(tf->getf()) );
}
//=============================================================================
@ -238,11 +235,14 @@ Node *ConvF2LNode::Ideal(PhaseGVN *phase, bool can_reshape) {
//------------------------------Value------------------------------------------
const Type* ConvHF2FNode::Value(PhaseGVN* phase) const {
const Type *t = phase->type( in(1) );
if( t == Type::TOP ) return Type::TOP;
if( t == TypeInt::SHORT ) return Type::FLOAT;
const TypeInt *ti = t->is_int();
if ( ti->is_con() ) return TypeF::make( SharedRuntime::hf2f( ti->get_con() ) );
if (t == Type::TOP) return Type::TOP;
if (t == TypeInt::SHORT) return Type::FLOAT;
if (StubRoutines::hf2f_adr() == nullptr) return bottom_type();
const TypeInt *ti = t->is_int();
if (ti->is_con()) {
return TypeF::make( StubRoutines::hf2f(ti->get_con()) );
}
return bottom_type();
}

View File

@ -108,7 +108,6 @@ class ConvF2HFNode : public Node {
virtual int Opcode() const;
virtual const Type *bottom_type() const { return TypeInt::SHORT; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual Node* Identity(PhaseGVN* phase);
virtual uint ideal_reg() const { return Op_RegI; }
};

View File

@ -179,6 +179,8 @@ class Abstract_VM_Version: AllStatic {
// Does platform support stack watermark barriers for concurrent stack processing?
constexpr static bool supports_stack_watermark_barrier() { return false; }
// Does platform support float16 instructions?
static bool supports_float16() { return false; }
static bool print_matching_lines_from_file(const char* filename, outputStream* st, const char* keywords_to_match[]);
protected:

View File

@ -445,97 +445,6 @@ JRT_LEAF(jdouble, SharedRuntime::l2d(jlong x))
return (jdouble)x;
JRT_END
// Reference implementation at src/java.base/share/classes/java/lang/Float.java:floatToFloat16
JRT_LEAF(jshort, SharedRuntime::f2hf(jfloat x))
union {jfloat f; jint i;} bits;
bits.f = x;
jint doppel = bits.i;
jshort sign_bit = (jshort) ((doppel & 0x80000000) >> 16);
if (g_isnan(x))
return (jshort)(sign_bit | 0x7c00 | (doppel & 0x007fe000) >> 13 | (doppel & 0x00001ff0) >> 4 | (doppel & 0x0000000f));
jfloat abs_f = (x >= 0.0f) ? x : (x * -1.0f);
// Overflow threshold is halffloat max value + 1/2 ulp
if (abs_f >= (65504.0f + 16.0f)) {
return (jshort)(sign_bit | 0x7c00); // Positive or negative infinity
}
// Smallest magnitude of Halffloat is 0x1.0p-24, half-way or smaller rounds to zero
if (abs_f <= (pow(2, -24) * 0.5f)) { // Covers float zeros and subnormals.
return sign_bit; // Positive or negative zero
}
jint exp = ((0x7f800000 & doppel) >> (24 - 1)) - 127;
// For binary16 subnormals, beside forcing exp to -15, retain
// the difference exp_delta = E_min - exp. This is the excess
// shift value, in addition to 13, to be used in the
// computations below. Further the (hidden) msb with value 1
// in f must be involved as well
jint exp_delta = 0;
jint msb = 0x00000000;
if (exp < -14) {
exp_delta = -14 - exp;
exp = -15;
msb = 0x00800000;
}
jint f_signif_bits = ((doppel & 0x007fffff) | msb);
// Significand bits as if using rounding to zero
jshort signif_bits = (jshort)(f_signif_bits >> (13 + exp_delta));
jint lsb = f_signif_bits & (1 << (13 + exp_delta));
jint round = f_signif_bits & (1 << (12 + exp_delta));
jint sticky = f_signif_bits & ((1 << (12 + exp_delta)) - 1);
if (round != 0 && ((lsb | sticky) != 0 )) {
signif_bits++;
}
return (jshort)(sign_bit | ( ((exp + 15) << 10) + signif_bits ) );
JRT_END
// Reference implementation at src/java.base/share/classes/java/lang/Float.java:float16ToFloat
JRT_LEAF(jfloat, SharedRuntime::hf2f(jshort x))
// Halffloat format has 1 signbit, 5 exponent bits and
// 10 significand bits
union {jfloat f; jint i;} bits;
jint hf_arg = (jint)x;
jint hf_sign_bit = 0x8000 & hf_arg;
jint hf_exp_bits = 0x7c00 & hf_arg;
jint hf_significand_bits = 0x03ff & hf_arg;
jint significand_shift = 13; //difference between float and halffloat precision
jfloat sign = (hf_sign_bit != 0) ? -1.0f : 1.0f;
// Extract halffloat exponent, remove its bias
jint hf_exp = (hf_exp_bits >> 10) - 15;
if (hf_exp == -15) {
// For subnormal values, return 2^-24 * significand bits
return (sign * (pow(2,-24)) * hf_significand_bits);
} else if (hf_exp == 16) {
if (hf_significand_bits == 0) {
bits.i = 0x7f800000;
return sign * bits.f;
} else {
bits.i = (hf_sign_bit << 16) | 0x7f800000 |
(hf_significand_bits << significand_shift);
return bits.f;
}
}
// Add the bias of float exponent and shift
jint float_exp_bits = (hf_exp + 127) << (24 - 1);
// Combine sign, exponent and significand bits
bits.i = (hf_sign_bit << 16) | float_exp_bits |
(hf_significand_bits << significand_shift);
return bits.f;
JRT_END
// Exception handling across interpreter/compiler boundaries
//

View File

@ -128,8 +128,6 @@ class SharedRuntime: AllStatic {
static jfloat d2f (jdouble x);
static jfloat l2f (jlong x);
static jdouble l2d (jlong x);
static jfloat hf2f(jshort x);
static jshort f2hf(jfloat x);
static jfloat i2f (jint x);
#ifdef __SOFTFP__

View File

@ -168,6 +168,9 @@ address StubRoutines::_dlibm_reduce_pi04l = nullptr;
address StubRoutines::_dlibm_tan_cot_huge = nullptr;
address StubRoutines::_dtan = nullptr;
address StubRoutines::_f2hf = nullptr;
address StubRoutines::_hf2f = nullptr;
address StubRoutines::_vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP] = {{nullptr}, {nullptr}};
address StubRoutines::_vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP] = {{nullptr}, {nullptr}};

View File

@ -249,6 +249,9 @@ class StubRoutines: AllStatic {
static address _dlibm_tan_cot_huge;
static address _dtan;
static address _f2hf;
static address _hf2f;
static address _cont_thaw;
static address _cont_returnBarrier;
static address _cont_returnBarrierExc;
@ -424,6 +427,24 @@ class StubRoutines: AllStatic {
static address dlibm_tan_cot_huge() { return _dlibm_tan_cot_huge; }
static address dtan() { return _dtan; }
// These are versions of the java.lang.Float::floatToFloat16() and float16ToFloat()
// methods which perform the same operations as the intrinsic version.
// They are used for constant folding in JIT compiler to ensure equivalence.
//
static address f2hf_adr() { return _f2hf; }
static address hf2f_adr() { return _hf2f; }
static jshort f2hf(jfloat x) {
assert(_f2hf != nullptr, "stub is not implemented on this platform");
typedef jshort (*f2hf_stub_t)(jfloat x);
return ((f2hf_stub_t)_f2hf)(x);
}
static jfloat hf2f(jshort x) {
assert(_hf2f != nullptr, "stub is not implemented on this platform");
typedef jfloat (*hf2f_stub_t)(jshort x);
return ((hf2f_stub_t)_hf2f)(x);
}
static address cont_thaw() { return _cont_thaw; }
static address cont_returnBarrier() { return _cont_returnBarrier; }
static address cont_returnBarrierExc(){return _cont_returnBarrierExc; }

View File

@ -0,0 +1,436 @@
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8289551 8302976
* @summary Verify conversion between float and the binary16 format
* @requires (vm.cpu.features ~= ".*avx512vl.*" | vm.cpu.features ~= ".*f16c.*") | os.arch=="aarch64"
* @requires vm.compiler1.enabled & vm.compiler2.enabled
* @requires vm.compMode != "Xcomp"
* @comment default run
* @run main Binary16Conversion
* @comment C1 JIT compilation only:
* @run main/othervm -Xcomp -XX:TieredStopAtLevel=1 -XX:CompileCommand=compileonly,Binary16Conversion::test* Binary16Conversion
* @comment C2 JIT compilation only:
* @run main/othervm -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,Binary16Conversion::test* Binary16Conversion
*/
public class Binary16Conversion {
public static final int FLOAT_SIGNIFICAND_WIDTH = 24;
public static void main(String... argv) {
System.out.println("Start ...");
short s = Float.floatToFloat16(0.0f); // Load Float class
int errors = 0;
errors += testBinary16RoundTrip();
// Note that helper methods do sign-symmetric testing
errors += testBinary16CardinalValues();
errors += testRoundFloatToBinary16();
errors += testRoundFloatToBinary16HalfWayCases();
errors += testRoundFloatToBinary16FullBinade();
errors += testAlternativeImplementation();
if (errors > 0)
throw new RuntimeException(errors + " errors");
}
/*
* Put all 16-bit values through a conversion loop and make sure
* the values are preserved (NaN bit patterns notwithstanding).
*/
private static int testBinary16RoundTrip() {
int errors = 0;
for (int i = Short.MIN_VALUE; i < Short.MAX_VALUE; i++) {
short s = (short)i;
float f = Float.float16ToFloat(s);
short s2 = Float.floatToFloat16(f);
if (!Binary16.equivalent(s, s2)) {
errors++;
System.out.println("Roundtrip failure on " +
Integer.toHexString(0xFFFF & (int)s) +
"\t got back " + Integer.toHexString(0xFFFF & (int)s2));
}
}
return errors;
}
private static int testBinary16CardinalValues() {
int errors = 0;
// Encode short value for different binary16 cardinal values as an
// integer-valued float.
float[][] testCases = {
{Binary16.POSITIVE_ZERO, +0.0f},
{Binary16.MIN_VALUE, 0x1.0p-24f},
{Binary16.MAX_SUBNORMAL, 0x1.ff8p-15f},
{Binary16.MIN_NORMAL, 0x1.0p-14f},
{Binary16.ONE, 1.0f},
{Binary16.MAX_VALUE, 65504.0f},
{Binary16.POSITIVE_INFINITY, Float.POSITIVE_INFINITY},
};
// Check conversions in both directions
// short -> float
for (var testCase : testCases) {
errors += compareAndReportError((short)testCase[0],
testCase[1]);
}
// float -> short
for (var testCase : testCases) {
errors += compareAndReportError(testCase[1],
(short)testCase[0]);
}
return errors;
}
private static int testRoundFloatToBinary16() {
int errors = 0;
float[][] testCases = {
// Test all combinations of LSB, round, and sticky bit
// LSB = 0, test combination of round and sticky
{0x1.ff8000p-1f, (short)0x3bfe}, // round = 0, sticky = 0
{0x1.ff8010p-1f, (short)0x3bfe}, // round = 0, sticky = 1
{0x1.ffa000p-1f, (short)0x3bfe}, // round = 1, sticky = 0
{0x1.ffa010p-1f, (short)0x3bff}, // round = 1, sticky = 1 => ++
// LSB = 1, test combination of round and sticky
{0x1.ffc000p-1f, Binary16.ONE-1}, // round = 0, sticky = 0
{0x1.ffc010p-1f, Binary16.ONE-1}, // round = 0, sticky = 1
{0x1.ffe000p-1f, Binary16.ONE}, // round = 1, sticky = 0 => ++
{0x1.ffe010p-1f, Binary16.ONE}, // round = 1, sticky = 1 => ++
// Test subnormal rounding
// Largest subnormal binary16 0x03ff => 0x1.ff8p-15f; LSB = 1
{0x1.ff8000p-15f, Binary16.MAX_SUBNORMAL}, // round = 0, sticky = 0
{0x1.ff8010p-15f, Binary16.MAX_SUBNORMAL}, // round = 0, sticky = 1
{0x1.ffc000p-15f, Binary16.MIN_NORMAL}, // round = 1, sticky = 0 => ++
{0x1.ffc010p-15f, Binary16.MIN_NORMAL}, // round = 1, sticky = 1 => ++
// Test rounding near binary16 MIN_VALUE
// Smallest in magnitude subnormal binary16 value 0x0001 => 0x1.0p-24f
// Half-way case,0x1.0p-25f, and smaller should round down to zero
{0x1.fffffep-26f, Binary16.POSITIVE_ZERO}, // nextDown in float
{0x1.000000p-25f, Binary16.POSITIVE_ZERO},
{0x1.000002p-25f, Binary16.MIN_VALUE}, // nextUp in float
{0x1.100000p-25f, Binary16.MIN_VALUE},
// Test rounding near overflow threshold
// Largest normal binary16 number 0x7bff => 0x1.ffcp15f; LSB = 1
{0x1.ffc000p15f, Binary16.MAX_VALUE}, // round = 0, sticky = 0
{0x1.ffc010p15f, Binary16.MAX_VALUE}, // round = 0, sticky = 1
{0x1.ffe000p15f, Binary16.POSITIVE_INFINITY}, // round = 1, sticky = 0 => ++
{0x1.ffe010p15f, Binary16.POSITIVE_INFINITY}, // round = 1, sticky = 1 => ++
};
for (var testCase : testCases) {
errors += compareAndReportError(testCase[0],
(short)testCase[1]);
}
return errors;
}
private static int testRoundFloatToBinary16HalfWayCases() {
int errors = 0;
// Test rounding of exact half-way cases between each pair of
// finite exactly-representable binary16 numbers. Also test
// rounding of half-way +/- ulp of the *float* value.
// Additionally, test +/- float ulp of the endpoints. (Other
// tests in this file make sure all short values round-trip so
// that doesn't need to be tested here.)
for (int i = Binary16.POSITIVE_ZERO; // 0x0000
i <= Binary16.MAX_VALUE; // 0x7bff
i += 2) { // Check every even/odd pair once
short lower = (short) i;
short upper = (short)(i+1);
float lowerFloat = Float.float16ToFloat(lower);
float upperFloat = Float.float16ToFloat(upper);
assert lowerFloat < upperFloat;
float midway = (lowerFloat + upperFloat) * 0.5f; // Exact midpoint
errors += compareAndReportError(Math.nextUp(lowerFloat), lower);
errors += compareAndReportError(Math.nextDown(midway), lower);
// Under round to nearest even, the midway point will
// round *down* to the (even) lower endpoint.
errors += compareAndReportError( midway, lower);
errors += compareAndReportError(Math.nextUp( midway), upper);
errors += compareAndReportError(Math.nextDown(upperFloat), upper);
}
// More testing around the overflow threshold
// Binary16.ulp(Binary16.MAX_VALUE) == 32.0f; test around Binary16.MAX_VALUE + 1/2 ulp
float binary16_MAX_VALUE = Float.float16ToFloat(Binary16.MAX_VALUE);
float binary16_MAX_VALUE_halfUlp = binary16_MAX_VALUE + 16.0f;
errors += compareAndReportError(Math.nextDown(binary16_MAX_VALUE), Binary16.MAX_VALUE);
errors += compareAndReportError( binary16_MAX_VALUE, Binary16.MAX_VALUE);
errors += compareAndReportError(Math.nextUp( binary16_MAX_VALUE), Binary16.MAX_VALUE);
// Binary16.MAX_VALUE is an "odd" value since its LSB = 1 so
// the half-way value greater than Binary16.MAX_VALUE should
// round up to the next even value, in this case Binary16.POSITIVE_INFINITY.
errors += compareAndReportError(Math.nextDown(binary16_MAX_VALUE_halfUlp), Binary16.MAX_VALUE);
errors += compareAndReportError( binary16_MAX_VALUE_halfUlp, Binary16.POSITIVE_INFINITY);
errors += compareAndReportError(Math.nextUp( binary16_MAX_VALUE_halfUlp), Binary16.POSITIVE_INFINITY);
return errors;
}
private static int compareAndReportError(float input,
short expected) {
// Round to nearest even is sign symmetric
return compareAndReportError0( input, expected) +
compareAndReportError0(-input, Binary16.negate(expected));
}
private static int compareAndReportError0(float input,
short expected) {
short actual = Float.floatToFloat16(input);
if (!Binary16.equivalent(actual, expected)) {
System.out.println("Unexpected result of converting " +
Float.toHexString(input) +
" to short. Expected 0x" + Integer.toHexString(0xFFFF & expected) +
" got 0x" + Integer.toHexString(0xFFFF & actual));
return 1;
}
return 0;
}
private static int compareAndReportError0(short input,
float expected) {
float actual = Float.float16ToFloat(input);
if (Float.compare(actual, expected) != 0) {
System.out.println("Unexpected result of converting " +
Integer.toHexString(input & 0xFFFF) +
" to float. Expected " + Float.toHexString(expected) +
" got " + Float.toHexString(actual));
return 1;
}
return 0;
}
private static int compareAndReportError(short input,
float expected) {
// Round to nearest even is sign symmetric
return compareAndReportError0( input, expected) +
compareAndReportError0(Binary16.negate(input), -expected);
}
private static int testRoundFloatToBinary16FullBinade() {
int errors = 0;
// For each float value between 1.0 and less than 2.0
// (i.e. set of float values with an exponent of 0), convert
// each value to binary16 and then convert that binary16 value
// back to float.
//
// Any exponent could be used; the maximum exponent for normal
// values would not exercise the full set of code paths since
// there is an up-front check on values that would overflow,
// which correspond to a ripple-carry of the significand that
// bumps the exponent.
short previous = (short)0;
for (int i = Float.floatToIntBits(1.0f);
i <= Float.floatToIntBits(Math.nextDown(2.0f));
i++) {
// (Could also express the loop control directly in terms
// of floating-point operations, incrementing by ulp(1.0),
// etc.)
float f = Float.intBitsToFloat(i);
short f_as_bin16 = Float.floatToFloat16(f);
short f_as_bin16_down = (short)(f_as_bin16 - 1);
short f_as_bin16_up = (short)(f_as_bin16 + 1);
// Across successive float values to convert to binary16,
// the binary16 results should be semi-monotonic,
// non-decreasing in this case.
// Only positive binary16 values so can compare using integer operations
if (f_as_bin16 < previous) {
errors++;
System.out.println("Semi-monotonicity violation observed on loat: " + Float.toHexString(f) + "/" + Integer.toHexString(i) + " " +
Integer.toHexString(0xffff & f_as_bin16) + " previous: " + Integer.toHexString(0xffff & previous) + " f_as_bin16: " + Integer.toHexString(0xffff & f_as_bin16));
}
// previous = f_as_bin16;
// If round-to-nearest was correctly done, when exactly
// mapped back to float, f_as_bin16 should be at least as
// close as either of its neighbors to the original value
// of f.
float f_prime_down = Float.float16ToFloat(f_as_bin16_down);
float f_prime = Float.float16ToFloat(f_as_bin16);
float f_prime_up = Float.float16ToFloat(f_as_bin16_up);
previous = f_as_bin16;
float f_prime_diff = Math.abs(f - f_prime);
if (f_prime_diff == 0.0) {
continue;
}
float f_prime_down_diff = Math.abs(f - f_prime_down);
float f_prime_up_diff = Math.abs(f - f_prime_up);
if (f_prime_diff > f_prime_down_diff ||
f_prime_diff > f_prime_up_diff) {
errors++;
System.out.println("Round-to-nearest violation on converting " +
Float.toHexString(f) + "/" + Integer.toHexString(i) + " to binary16 and back: " + Integer.toHexString(0xffff & f_as_bin16) + " f_prime: " + Float.toHexString(f_prime));
}
}
return errors;
}
private static int testAlternativeImplementation() {
int errors = 0;
// For exhaustive test of all float values use
// for (long ell = Integer.MIN_VALUE; ell <= Integer.MAX_VALUE; ell++) {
for (long ell = Float.floatToIntBits(2.0f);
ell <= Float.floatToIntBits(4.0f);
ell++) {
float f = Float.intBitsToFloat((int)ell);
short s1 = Float.floatToFloat16(f);
short s2 = testAltFloatToFloat16(f);
if (s1 != s2) {
errors++;
System.out.println("Different conversion of float value " + Float.toHexString(f));
}
}
return errors;
}
/*
* Rely on float operations to do rounding in both normal and
* subnormal binary16 cases.
*/
public static short testAltFloatToFloat16(float f) {
int doppel = Float.floatToRawIntBits(f);
short sign_bit = (short)((doppel & 0x8000_0000) >> 16);
if (Float.isNaN(f)) {
// Preserve sign and attempt to preserve significand bits
return (short)(sign_bit
| 0x7c00 // max exponent + 1
// Preserve high order bit of float NaN in the
// binary16 result NaN (tenth bit); OR in remaining
// bits into lower 9 bits of binary 16 significand.
| (doppel & 0x007f_e000) >> 13 // 10 bits
| (doppel & 0x0000_1ff0) >> 4 // 9 bits
| (doppel & 0x0000_000f)); // 4 bits
}
float abs_f = Math.abs(f);
// The overflow threshold is binary16 MAX_VALUE + 1/2 ulp
if (abs_f >= (65504.0f + 16.0f) ) {
return (short)(sign_bit | 0x7c00); // Positive or negative infinity
} else {
// Smallest magnitude nonzero representable binary16 value
// is equal to 0x1.0p-24; half-way and smaller rounds to zero.
if (abs_f <= 0x1.0p-25f) { // Covers float zeros and subnormals.
return sign_bit; // Positive or negative zero
}
// Dealing with finite values in exponent range of
// binary16 (when rounding is done, could still round up)
int exp = Math.getExponent(f);
assert -25 <= exp && exp <= 15;
short signif_bits;
if (exp <= -15) { // scale down to float subnormal range to do rounding
// Use a float multiply to compute the correct
// trailing significand bits for a binary16 subnormal.
//
// The exponent range of normalized binary16 subnormal
// values is [-24, -15]. The exponent range of float
// subnormals is [-149, -140]. Multiply abs_f down by
// 2^(-125) -- since (-125 = -149 - (-24)) -- so that
// the trailing bits of a subnormal float represent
// the correct trailing bits of a binary16 subnormal.
exp = -15; // Subnormal encoding using -E_max.
float f_adjust = abs_f * 0x1.0p-125f;
// In case the significand rounds up and has a carry
// propagate all the way up, take the bottom 11 bits
// rather than bottom 10 bits. Adding this value,
// rather than OR'ing htis value, will cause the right
// exponent adjustment.
signif_bits = (short)(Float.floatToRawIntBits(f_adjust) & 0x07ff);
return (short)(sign_bit | ( ((exp + 15) << 10) + signif_bits ) );
} else {
// Scale down to subnormal range to round off excess bits
int scalingExp = -139 - exp;
float scaled = Math.scalb(Math.scalb(f, scalingExp),
-scalingExp);
exp = Math.getExponent(scaled);
doppel = Float.floatToRawIntBits(scaled);
signif_bits = (short)((doppel & 0x007f_e000) >>
(FLOAT_SIGNIFICAND_WIDTH - 11));
return (short)(sign_bit | ( ((exp + 15) << 10) | signif_bits ) );
}
}
}
public static class Binary16 {
public static final short POSITIVE_INFINITY = (short)0x7c00;
public static final short MAX_VALUE = 0x7bff;
public static final short ONE = 0x3c00;
public static final short MIN_NORMAL = 0x0400;
public static final short MAX_SUBNORMAL = 0x03ff;
public static final short MIN_VALUE = 0x0001;
public static final short POSITIVE_ZERO = 0x0000;
public static boolean isNaN(short binary16) {
return ((binary16 & 0x7c00) == 0x7c00) // Max exponent and...
&& ((binary16 & 0x03ff) != 0 ); // significand nonzero.
}
public static short negate(short binary16) {
return (short)(binary16 ^ 0x8000 ); // Flip only sign bit.
}
public static boolean equivalent(short bin16_1, short bin16_2) {
return (bin16_1 == bin16_2) ||
isNaN(bin16_1) && isNaN(bin16_2);
}
}
}

View File

@ -0,0 +1,161 @@
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8289551 8302976
* @summary Verify NaN sign and significand bits are preserved across conversions
* @requires (vm.cpu.features ~= ".*avx512vl.*" | vm.cpu.features ~= ".*f16c.*") | os.arch=="aarch64"
* @requires vm.compiler1.enabled & vm.compiler2.enabled
* @requires vm.compMode != "Xcomp"
* @library /test/lib /
*
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -Xmixed -XX:-BackgroundCompilation -XX:-UseOnStackReplacement
* -XX:CompileThresholdScaling=1000.0 Binary16ConversionNaN
*/
/*
* The behavior tested below is an implementation property not
* required by the specification. It would be acceptable for this
* information to not be preserved (as long as a NaN is returned) if,
* say, a intrinsified version using native hardware instructions
* behaved differently.
*
* If that is the case, this test should be modified to disable
* intrinsics or to otherwise not run on platforms with an differently
* behaving intrinsic.
*/
import compiler.whitebox.CompilerWhiteBoxTest;
import jdk.test.whitebox.WhiteBox;
import java.lang.reflect.Method;
public class Binary16ConversionNaN {
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
/*
* Put all 16-bit NaN values through a conversion loop and make
* sure the significand, sign, and exponent are all preserved.
*/
public static void main(String... argv) throws NoSuchMethodException {
int errors = 0;
final int NAN_EXPONENT = 0x7c00;
final int SIGN_BIT = 0x8000;
// First, run with Interpreter only to collect "gold" data.
// Glags -Xmixed -XX:CompileThresholdScaling=1000.0 are used
// to prevent compilation during this phase.
short[] pVal = new short[1024];
short[] pRes = new short[1024];
short[] nVal = new short[1024];
short[] nRes = new short[1024];
// A NaN has a nonzero significand
for (int i = 1; i <= 0x3ff; i++) {
short binary16NaN = (short)(NAN_EXPONENT | i);
assert isNaN(binary16NaN);
short s1 = testRoundTrip(binary16NaN);
errors += verify(binary16NaN, s1);
pVal[i] = binary16NaN;
pRes[i] = s1;
short binary16NegNaN = (short)(SIGN_BIT | binary16NaN);
short s2 = testRoundTrip(binary16NegNaN);
errors += verify(binary16NegNaN, s2);
nVal[i] = binary16NegNaN;
nRes[i] = s2;
}
if (errors > 0) { // Exit if Interpreter failed
throw new RuntimeException(errors + " errors");
}
Method test_method = Binary16ConversionNaN.class.getDeclaredMethod("testRoundTrip", short.class);
// Compile with C1 and compare results
WHITE_BOX.enqueueMethodForCompilation(test_method, CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE);
if (!WHITE_BOX.isMethodCompiled(test_method)) {
throw new RuntimeException("test is not compiled by C1");
}
for (int i = 1; i <= 0x3ff; i++) {
short s1 = testRoundTrip(pVal[i]);
errors += verifyCompiler(pRes[i], s1, "C1");
short s2 = testRoundTrip(nVal[i]);
errors += verifyCompiler(nRes[i], s2, "C1");
}
WHITE_BOX.deoptimizeMethod(test_method);
// Compile with C2 and compare results
WHITE_BOX.enqueueMethodForCompilation(test_method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
if (!WHITE_BOX.isMethodCompiled(test_method)) {
throw new RuntimeException("test is not compiled by C2");
}
for (int i = 1; i <= 0x3ff; i++) {
short s1 = testRoundTrip(pVal[i]);
errors += verifyCompiler(pRes[i], s1, "C2");
short s2 = testRoundTrip(nVal[i]);
errors += verifyCompiler(nRes[i], s2, "C2");
}
if (errors > 0) {
throw new RuntimeException(errors + " errors");
}
}
private static boolean isNaN(short binary16) {
return ((binary16 & 0x7c00) == 0x7c00) // Max exponent and...
&& ((binary16 & 0x03ff) != 0 ); // significand nonzero.
}
private static short testRoundTrip(short i) {
float f = Float.float16ToFloat(i);
return Float.floatToFloat16(f);
}
private static int verify(short s, short s2) {
int errors = 0;
if ((s & ~0x0200) != (s2 & ~0x0200)) { // ignore QNaN bit
errors++;
System.out.println("Roundtrip failure on NaN value " +
Integer.toHexString(0xFFFF & (int)s) +
"\t got back " + Integer.toHexString(0xFFFF & (int)s2));
}
return errors;
}
private static int verifyCompiler(short s, short s2, String name) {
int errors = 0;
if (s != s2) {
errors++;
System.out.println("Roundtrip failure on NaN value " +
Integer.toHexString(0xFFFF & (int)s) +
"\t got back " + Integer.toHexString(0xFFFF & (int)s2) +
"\t from " + name + " code");
}
return errors;
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8302976
* @summary Verify conversion between float and the binary16 format
* @requires (vm.cpu.features ~= ".*avx512vl.*" | vm.cpu.features ~= ".*f16c.*") | os.arch == "aarch64"
* @requires vm.compiler1.enabled & vm.compiler2.enabled
* @requires vm.compMode != "Xcomp"
* @comment default run:
* @run main TestAllFloat16ToFloat
* @comment disable intrinsics:
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_float16ToFloat,_floatToFloat16 TestAllFloat16ToFloat
* @comment eager JIT compilation:
* @run main/othervm -XX:CompileCommand=compileonly,TestAllFloat16ToFloat::test* -Xbatch TestAllFloat16ToFloat
* @comment C2 JIT compilation only:
* @run main/othervm -XX:CompileCommand=compileonly,TestAllFloat16ToFloat::test* -Xbatch -XX:-TieredCompilation TestAllFloat16ToFloat
* @comment C1 JIT compilation only:
* @run main/othervm -XX:CompileCommand=compileonly,TestAllFloat16ToFloat::test* -Xbatch -XX:TieredStopAtLevel=1 TestAllFloat16ToFloat
*/
public class TestAllFloat16ToFloat {
public static short testFloatToFloat16(float f) {
return Float.floatToFloat16(f);
}
public static float testFloat16ToFloat(short s) {
return Float.float16ToFloat(s);
}
public static short testRoundTrip(short s) {
return Float.floatToFloat16(Float.float16ToFloat(s));
}
public static void verify(short sVal, float fVal, short sRes) {
if (sRes != sVal) {
if (!Float.isNaN(fVal) || ((sRes & ~0x0200) != (sVal & ~0x0200)) ) {
String fVal_hex = Integer.toHexString(Float.floatToRawIntBits(fVal));
String sRes_hex = Integer.toHexString(sRes & 0xffff);
String sVal_hex = Integer.toHexString(sVal & 0xffff);
throw new RuntimeException("Inconsistent result for Float.floatToFloat16(" + fVal + "/" + fVal_hex + "): " + sRes_hex + " != " + sVal_hex);
}
}
}
public static void run() {
// Testing all float16 values.
for (short sVal = Short.MIN_VALUE; sVal < Short.MAX_VALUE; ++sVal) {
float fVal = Float.float16ToFloat(sVal);
short sRes = testFloatToFloat16(fVal);
verify(sVal, fVal, sRes);
float fRes = testFloat16ToFloat(sVal);
if (!Float.isNaN(fRes) && fRes != fVal) {
String sVal_hex = Integer.toHexString(sVal & 0xffff);
String fRes_hex = Integer.toHexString(Float.floatToRawIntBits(fRes));
String fVal_hex = Integer.toHexString(Float.floatToRawIntBits(fVal));
throw new RuntimeException("Inconsistent result for Float.float16ToFloat(" + sVal_hex + "): " + fRes + "/" + fRes_hex + " != " + fVal + "/" + fVal_hex);
}
sRes = testRoundTrip(sVal);
verify(sVal, fVal, sRes);
if (Float.floatToFloat16(fRes) != Float.floatToFloat16(fVal)) {
String sVal_hex = Integer.toHexString(sVal & 0xffff);
String sfRes_hex = Integer.toHexString(Float.floatToFloat16(fRes) & 0xffff);
String sfVal_hex = Integer.toHexString(Float.floatToFloat16(fVal)& 0xffff);
throw new RuntimeException("Inconsistent result for Float.float16ToFloat(" + sVal_hex + "): " + sfRes_hex + " != " + sfVal_hex);
}
}
}
public static void main(String[] args) {
// Run twice to trigger compilation
for (int i = 0; i < 2; i++) {
run();
}
}
}

View File

@ -0,0 +1,166 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8302976
* @summary Verify conversion cons between float and the binary16 format
* @requires (vm.cpu.features ~= ".*avx512vl.*" | vm.cpu.features ~= ".*f16c.*") | os.arch=="aarch64"
* @requires vm.compiler1.enabled & vm.compiler2.enabled
* @requires vm.compMode != "Xcomp"
* @comment default run:
* @run main TestConstFloat16ToFloat
* @comment C1 JIT compilation only:
* @run main/othervm -Xcomp -XX:CompileCommand=compileonly,TestConstFloat16ToFloat::test* -XX:TieredStopAtLevel=1 TestConstFloat16ToFloat
* @comment C2 JIT compilation only:
* @run main/othervm -Xcomp -XX:CompileCommand=compileonly,TestConstFloat16ToFloat::test* -XX:-TieredCompilation TestConstFloat16ToFloat
*/
public class TestConstFloat16ToFloat {
public static class Binary16 {
public static final short POSITIVE_INFINITY = (short)0x7c00;
public static final short MAX_VALUE = 0x7bff;
public static final short ONE = 0x3c00;
public static final short MIN_NORMAL = 0x0400;
public static final short MAX_SUBNORMAL = 0x03ff;
public static final short MIN_VALUE = 0x0001;
public static final short POSITIVE_ZERO = 0x0000;
}
static final short[] sCon = {
Short.MIN_VALUE,
Short.MIN_VALUE + 1,
-1,
0,
+1,
Short.MAX_VALUE - 1,
Short.MAX_VALUE,
Binary16.MIN_VALUE,
Binary16.MIN_NORMAL,
Binary16.POSITIVE_ZERO,
Binary16.ONE,
Binary16.MAX_VALUE,
Binary16.MAX_SUBNORMAL,
Binary16.POSITIVE_INFINITY
};
public final static class BinaryF16 {
public static final float POSITIVE_INFINITY = Float.POSITIVE_INFINITY;
public static final float MAX_VALUE = 65504.0f;
public static final float ONE = 1.0f;
public static final float MIN_NORMAL = 0x1.0p-14f;
public static final float MAX_SUBNORMAL = 0x1.ff8p-15f;
public static final float MIN_VALUE = 0x1.0p-24f;
public static final float POSITIVE_ZERO = +0x0f;
}
static float[] fCon = {
0.0f - BinaryF16.POSITIVE_INFINITY,
0.0f - BinaryF16.MAX_VALUE,
0.0f - BinaryF16.MAX_SUBNORMAL,
0.0f - BinaryF16.MIN_VALUE,
0.0f - BinaryF16.MIN_NORMAL,
-1.0f,
-0.0f,
BinaryF16.MIN_VALUE,
BinaryF16.MIN_NORMAL,
BinaryF16.POSITIVE_ZERO,
BinaryF16.ONE,
BinaryF16.MAX_VALUE,
BinaryF16.MAX_SUBNORMAL,
BinaryF16.POSITIVE_INFINITY
};
// Testing some constant values (optimized by C2).
public static void testFloat16Const(float[] fRes) {
fRes[ 0] = Float.float16ToFloat(Short.MIN_VALUE);
fRes[ 1] = Float.float16ToFloat((short)(Short.MIN_VALUE + 1));
fRes[ 2] = Float.float16ToFloat((short)-1);
fRes[ 3] = Float.float16ToFloat((short)0);
fRes[ 4] = Float.float16ToFloat((short)+1);
fRes[ 5] = Float.float16ToFloat((short)(Short.MAX_VALUE - 1));
fRes[ 6] = Float.float16ToFloat(Short.MAX_VALUE);
fRes[ 7] = Float.float16ToFloat(Binary16.MIN_VALUE);
fRes[ 8] = Float.float16ToFloat(Binary16.MIN_NORMAL);
fRes[ 9] = Float.float16ToFloat(Binary16.POSITIVE_ZERO);
fRes[10] = Float.float16ToFloat(Binary16.ONE);
fRes[11] = Float.float16ToFloat(Binary16.MAX_VALUE);
fRes[12] = Float.float16ToFloat(Binary16.MAX_SUBNORMAL);
fRes[13] = Float.float16ToFloat(Binary16.POSITIVE_INFINITY);
}
public static void testFloatConst(short[] sRes) {
sRes[ 0] = Float.floatToFloat16(0.0f - BinaryF16.POSITIVE_INFINITY);
sRes[ 1] = Float.floatToFloat16(0.0f - BinaryF16.MAX_VALUE);
sRes[ 2] = Float.floatToFloat16(0.0f - BinaryF16.MAX_SUBNORMAL);
sRes[ 3] = Float.floatToFloat16(0.0f - BinaryF16.MIN_VALUE);
sRes[ 4] = Float.floatToFloat16(0.0f - BinaryF16.MIN_NORMAL);
sRes[ 5] = Float.floatToFloat16(-1.0f);
sRes[ 6] = Float.floatToFloat16(-0.0f);
sRes[ 7] = Float.floatToFloat16(BinaryF16.MIN_VALUE);
sRes[ 8] = Float.floatToFloat16(BinaryF16.MIN_NORMAL);
sRes[ 9] = Float.floatToFloat16(BinaryF16.POSITIVE_ZERO);
sRes[10] = Float.floatToFloat16(BinaryF16.ONE);
sRes[11] = Float.floatToFloat16(BinaryF16.MAX_VALUE);
sRes[12] = Float.floatToFloat16(BinaryF16.MAX_SUBNORMAL);
sRes[13] = Float.floatToFloat16(BinaryF16.POSITIVE_INFINITY);
}
public static void run() {
short s = Float.floatToFloat16(0.0f); // Load Float class
// Testing constant float16 values.
float[] fRes = new float[sCon.length];
testFloat16Const(fRes);
for (int i = 0; i < sCon.length; i++) {
float fVal = Float.float16ToFloat(sCon[i]);
if (Float.floatToRawIntBits(fRes[i]) != Float.floatToRawIntBits(fVal)) {
String cVal_hex = Integer.toHexString(sCon[i] & 0xffff);
String fRes_hex = Integer.toHexString(Float.floatToRawIntBits(fRes[i]));
String fVal_hex = Integer.toHexString(Float.floatToRawIntBits(fVal));
throw new RuntimeException("Inconsistent result for Float.float16ToFloat(" + cVal_hex + "): " + fRes[i] + "/" + fRes_hex + " != " + fVal + "/" + fVal_hex);
}
}
// Testing constant float values.
short[] sRes = new short[fCon.length];
testFloatConst(sRes);
for (int i = 0; i < fCon.length; i++) {
short sVal = Float.floatToFloat16(fCon[i]);
if (sRes[i] != sVal) {
String cVal_hex = Integer.toHexString(Float.floatToRawIntBits(fCon[i]));
String sRes_hex = Integer.toHexString(sRes[i] & 0xffff);
String sVal_hex = Integer.toHexString(sVal & 0xffff);
throw new RuntimeException("Inconsistent result for Float.floatToFloat16(" + fCon[i] + "/" + cVal_hex + "): " + sRes_hex + " != " + sVal_hex);
}
}
}
public static void main(String[] args) {
// Run twice to trigger compilation
for (int i = 0; i < 2; i++) {
run();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,6 @@
* @bug 8289551
* @requires (os.arch != "x86" & os.arch != "i386") | vm.opt.UseSSE == "null" | vm.opt.UseSSE > 0
* @summary Verify NaN sign and significand bits are preserved across conversions
* @run main/othervm -XX:-TieredCompilation -XX:CompileThresholdScaling=0.1 Binary16ConversionNaN
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
* -XX:DisableIntrinsic=_float16ToFloat,_floatToFloat16 Binary16ConversionNaN
*/