blob: 0297f8b8892bd42e06609c99893900b95cee95ce [file] [log] [blame]
/* brig-cvt-inst-handler.cc -- brig cvt (convert) instruction handling
Copyright (C) 2016-2017 Free Software Foundation, Inc.
Contributed by Pekka Jaaskelainen <pekka.jaaskelainen@parmance.com>
for General Processor Tech.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC 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
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include <sstream>
#include "brig-code-entry-handler.h"
#include "gimple-expr.h"
#include "errors.h"
#include "convert.h"
#include "tree-pretty-print.h"
#include "print-tree.h"
#include "diagnostic-core.h"
#include "brig-util.h"
const BrigAluModifier8_t *
brig_cvt_inst_handler::modifier (const BrigBase *base) const
{
const BrigInstCvt *inst = (const BrigInstCvt *) base;
return &inst->modifier;
}
const BrigRound8_t *
brig_cvt_inst_handler::round (const BrigBase *base) const
{
const BrigInstCvt *inst = (const BrigInstCvt *) base;
return &inst->round;
}
size_t
brig_cvt_inst_handler::generate (const BrigBase *base)
{
/* In cvt instructions there can be at least four data types involved:
- the input register type
- the output register type
- the conversion source type
- the conversion destination type
*/
const BrigInstBase *brig_inst
= (const BrigInstBase *) &((const BrigInstBasic *) base)->base;
const BrigInstCvt *cvt_inst = (const BrigInstCvt *) base;
const BrigAluModifier8_t *inst_modifier = modifier (base);
const bool FTZ = inst_modifier != NULL && (*inst_modifier) & BRIG_ALU_FTZ;
/* The conversion source type. */
tree src_type = get_tree_expr_type_for_hsa_type (cvt_inst->sourceType);
bool src_is_fp16 = cvt_inst->sourceType == BRIG_TYPE_F16;
/* The conversion destination type. */
tree dest_type = gccbrig_tree_type_for_hsa_type (brig_inst->type);
bool dest_is_fp16 = brig_inst->type == BRIG_TYPE_F16;
if (!dest_type || !src_type)
{
gcc_unreachable ();
return base->byteCount;
}
tree_stl_vec operands = build_operands (*brig_inst);
tree &input = operands.at (1);
tree &output = operands.at (0);
size_t conv_src_size = int_size_in_bytes (src_type);
size_t conv_dst_size = int_size_in_bytes (dest_type);
size_t src_reg_size = int_size_in_bytes (TREE_TYPE (input));
/* The input register can be of different type&size than the
conversion input size. First cast the input to the conversion
input type. These casts are always bitcasts which can be
expressed as casts between different unsigned integers. */
if (src_reg_size != conv_src_size)
{
tree unsigned_int_type = NULL_TREE;
if (INTEGRAL_TYPE_P (src_type))
unsigned_int_type = unsigned_type_for (src_type);
else /* Find a matching size int type for the REAL type. */
{
if (conv_src_size == 2)
unsigned_int_type = gccbrig_tree_type_for_hsa_type (BRIG_TYPE_U16);
else if (conv_src_size == 4)
unsigned_int_type = gccbrig_tree_type_for_hsa_type (BRIG_TYPE_U32);
else if (conv_src_size == 8)
unsigned_int_type = gccbrig_tree_type_for_hsa_type (BRIG_TYPE_U64);
else
gcc_unreachable ();
}
input = convert_to_integer (unsigned_int_type, input);
}
if (src_is_fp16)
input = build_h2f_conversion (input);
/* Flush the float operand to zero if indicated with 'ftz'. */
if (FTZ && SCALAR_FLOAT_TYPE_P (src_type))
{
tree casted_input = build_reinterpret_cast (src_type, input);
input = flush_to_zero (src_is_fp16) (*this, casted_input);
}
tree conversion_result = NULL_TREE;
if (brig_inst->type == BRIG_TYPE_B1)
{
/* When the destination is b1, cvt does a 'ztest' operation which is
defined as a != 0 for integers and similarly (!= 0.0f) for floats. */
if (INTEGRAL_TYPE_P (src_type))
{
/* Generate an integer not equal operation. */
conversion_result = build2 (NE_EXPR, TREE_TYPE (input), input,
build_int_cst (TREE_TYPE (input), 0));
}
else
{
/* For REAL source types, ztest returns 1 if the value is not +- 0.0f.
We can perform this check with an integer comparison after
masking away the sign bit from a correct position. This is safer
than using absf because of exceptions in case of a NaN
input (NaN exceptions are not generated with cvt). */
tree unsigned_int_type = NULL_TREE;
/* Bit battern with all but the upper bit 1. */
tree and_mask = NULL_TREE;
if (conv_src_size == 2)
{
unsigned_int_type = gccbrig_tree_type_for_hsa_type (BRIG_TYPE_U16);
and_mask = build_int_cst (unsigned_int_type, 0x7FFF);
}
else if (conv_src_size == 4)
{
unsigned_int_type = gccbrig_tree_type_for_hsa_type (BRIG_TYPE_U32);
and_mask = build_int_cst (unsigned_int_type, 0x7FFFFFFF);
}
else if (conv_src_size == 8)
{
unsigned_int_type = gccbrig_tree_type_for_hsa_type (BRIG_TYPE_U64);
and_mask = build_int_cst (unsigned_int_type, 0x7FFFFFFFFFFFFFFF);
}
else
gcc_unreachable ();
tree casted_input = build_reinterpret_cast (unsigned_int_type, input);
tree masked_input
= build2 (BIT_AND_EXPR, unsigned_int_type, casted_input, and_mask);
conversion_result
= build2 (NE_EXPR, TREE_TYPE (masked_input), masked_input,
build_int_cst (unsigned_int_type, 0));
}
/* The result from the comparison is a boolean, convert it to such. */
conversion_result
= convert_to_integer (gccbrig_tree_type_for_hsa_type (BRIG_TYPE_B1),
conversion_result);
}
else if (dest_is_fp16)
{
tree casted_input = build_reinterpret_cast (src_type, input);
conversion_result
= convert_to_real (brig_to_generic::s_fp32_type, casted_input);
if (FTZ)
conversion_result = flush_to_zero (true) (*this, conversion_result);
conversion_result = build_f2h_conversion (conversion_result);
}
else if (SCALAR_FLOAT_TYPE_P (dest_type))
{
tree casted_input = build_reinterpret_cast (src_type, input);
conversion_result = convert_to_real (dest_type, casted_input);
}
else if (INTEGRAL_TYPE_P (dest_type) && INTEGRAL_TYPE_P (src_type))
{
conversion_result = extend_int (input, dest_type, src_type);
}
else if (INTEGRAL_TYPE_P (dest_type) && SCALAR_FLOAT_TYPE_P (src_type))
{
if (cvt_inst->round == BRIG_ROUND_INTEGER_ZERO_SAT)
{
/* Use builtins for the saturating conversions. */
#undef DEF_HSAIL_SAT_BUILTIN
#undef DEF_HSAIL_BUILTIN
#undef DEF_HSAIL_ATOMIC_BUILTIN
#undef DEF_HSAIL_INTR_BUILTIN
#undef DEF_HSAIL_CVT_ZEROI_SAT_BUILTIN
tree builtin = NULL_TREE;
BrigType16_t src_arith_type
= src_is_fp16
? (BrigType16_t) BRIG_TYPE_F32 : cvt_inst->sourceType;
#define DEF_HSAIL_CVT_ZEROI_SAT_BUILTIN(ENUM, HSAIL_DST_TYPE, HSAIL_SRC_TYPE, \
NAME, TYPE, ATTRS) \
if (brig_inst->type == HSAIL_DST_TYPE \
&& src_arith_type == HSAIL_SRC_TYPE) \
builtin = builtin_decl_explicit (ENUM); \
else
#include "brig-builtins.def"
gcc_unreachable ();
tree casted_input = build_reinterpret_cast (src_type, input);
conversion_result
= call_builtin (builtin, 1, dest_type, src_type, casted_input);
}
else
{
tree casted_input = build_reinterpret_cast (src_type, input);
/* Perform the int to float conversion. */
conversion_result = convert_to_integer (dest_type, casted_input);
}
/* The converted result is finally extended to the target register
width, using the same sign as the destination. */
conversion_result
= convert_to_integer (TREE_TYPE (output), conversion_result);
}
else
{
/* Just use CONVERT_EXPR and hope for the best. */
tree casted_input = build_reinterpret_cast (dest_type, input);
conversion_result = build1 (CONVERT_EXPR, dest_type, casted_input);
}
size_t dst_reg_size = int_size_in_bytes (TREE_TYPE (output));
tree assign = NULL_TREE;
/* The output register can be of different type&size than the
conversion output size. Cast it to the register variable type. */
if (dst_reg_size > conv_dst_size)
{
tree casted_output
= build1 (CONVERT_EXPR, TREE_TYPE (output), conversion_result);
assign = build2 (MODIFY_EXPR, TREE_TYPE (output), output, casted_output);
}
else
{
tree casted_output
= build_reinterpret_cast (TREE_TYPE (output), conversion_result);
assign = build2 (MODIFY_EXPR, TREE_TYPE (output), output, casted_output);
}
m_parent.m_cf->append_statement (assign);
return base->byteCount;
}