const int i = static_cast
What is actually behind the static_cast?
The magic of static_cast starts in
tree
build_static_cast (tree type, tree expr, tsubst_flags_t complain)
{
tree result;
bool valid_p;
if (type == error_mark_node || expr == error_mark_node)
return error_mark_node;
if (processing_template_decl)
{
expr = build_min (STATIC_CAST_EXPR, type, expr);
/* We don't know if it will or will not have side effects. */
TREE_SIDE_EFFECTS (expr) = 1;
return convert_from_reference (expr);
}
/* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
Strip such NOP_EXPRs if VALUE is being used in non-lvalue context. */
if (TREE_CODE (type) != REFERENCE_TYPE
&& TREE_CODE (expr) == NOP_EXPR
&& TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0)))
expr = TREE_OPERAND (expr, 0);
result = build_static_cast_1 (type, expr, /*c_cast_p=*/false, &valid_p,
complain);
if (valid_p)
return result;
if (complain & tf_error)
error ("invalid static_cast from type %qT to type %qT",
TREE_TYPE (expr), type);
return error_mark_node;
}
There, for example, we can see g++'s "invalid static_cast ..." error message.
build_static_cast is a wrapper around build_static_cast_1, that, in turn,
is a big "switch", since it should deal with all kind of derived to
base casts, user defined casts, type instantiation, etc
static tree
build_static_cast_1 (tree type, tree expr, bool c_cast_p,
bool *valid_p, tsubst_flags_t complain)
For example:
/* "An lvalue of type cv1 T1 can be cast to type rvalue reference to
cv2 T2 if cv2 T2 is reference-compatible with cv1 T1 (8.5.3)." */
if (TREE_CODE (type) == REFERENCE_TYPE
&& TYPE_REF_IS_RVALUE (type)
&& real_lvalue_p (expr)
&& reference_related_p (TREE_TYPE (type), intype)
&& (c_cast_p || at_least_as_qualified_p (TREE_TYPE (type), intype)))
{
expr = build_typed_address (expr, type);
return convert_from_reference (expr);
}
From here we're getting to gcc/fold-const.c
Place where tons of interesting stuff are, for example, fold_convert_loc.
Which is, once again, a number of "switches" (convert from INTEGER to
CONST INTEGER, etc.) :
Convert expression ARG to type TYPE. Used by the middle-end for
simple conversions in preference to calling the front-end's convert.
fold_convert_loc (location_t loc, tree type, tree arg)
[..]
switch (TREE_CODE (type))
{
case POINTER_TYPE:
case REFERENCE_TYPE:
/* Handle conversions between pointers to different address spaces. */
if (POINTER_TYPE_P (orig)
&& (TYPE_ADDR_SPACE (TREE_TYPE (type))
!= TYPE_ADDR_SPACE (TREE_TYPE (orig))))
return fold_build1_loc (loc, ADDR_SPACE_CONVERT_EXPR, type, arg);
or
case INTEGER_TYPE: case ENUMERAL_TYPE: case BOOLEAN_TYPE:
case OFFSET_TYPE:
if (TREE_CODE (arg) == INTEGER_CST)
{
tem = fold_convert_const (NOP_EXPR, type, arg);
if (tem != NULL_TREE)
return tem;
}
Since we're requesting cast to const int -- the right function to call
is fold_convert_const, which is getting called from
Fold a unary expression of code CODE and type TYPE with operand
OP0. Return the folded expression if folding is successful.
Otherwise, return NULL_TREE.
fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0)
[..]
switch (code)
{
case PAREN_EXPR:
/* Re-association barriers around constants and other re-association
barriers can be removed. */
if (CONSTANT_CLASS_P (op0)
|| TREE_CODE (op0) == PAREN_EXPR)
return fold_convert_loc (loc, type, op0);
return NULL_TREE;
CASE_CONVERT:
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
[..]
tem = fold_convert_const (code, type, op0);
return tem ? tem : NULL_TREE;
fold_convert_const decides what conversion exactly should be
performed (if any):
fold_convert_const (enum tree_code code, tree type, tree arg1)
[..]
if (POINTER_TYPE_P (type) || INTEGRAL_TYPE_P (type)
|| TREE_CODE (type) == OFFSET_TYPE)
{
if (TREE_CODE (arg1) == INTEGER_CST)
return fold_convert_const_int_from_int (type, arg1);
else if (TREE_CODE (arg1) == REAL_CST)
return fold_convert_const_int_from_real (code, type, arg1);
else if (TREE_CODE (arg1) == FIXED_CST)
return fold_convert_const_int_from_fixed (type, arg1);
}
[..]
which is finally getting closer to a cast itself by
real_to_integer2 (HOST_WIDE_INT *plow, HOST_WIDE_INT *phigh,
const REAL_VALUE_TYPE *r)
call in gcc/real.c
The floating point model used internally is not exactly IEEE 754
compliant, and close to the description in the ISO C99 standard,
section 5.2.4.2.2 Characteristics of floating types.
Specifically
x = s * b^e * \sum_{k=1}^p f_k * b^{-k}
where
s = sign (+- 1)
b = base or radix, here always 2
e = exponent
p = precision (the number of base-b digits in the significand)
f_k = the digits of the significand.
We differ from typical IEEE 754 encodings in that the entire
significand is fractional. Normalized significands are in the
range [0.5, 1.0).
A requirement of the model is that P be larger than the largest
supported target floating-point type by at least 2 bits. This gives
us proper rounding when we truncate to the target type. In addition,
E must be large enough to hold the smallest supported denormal number
in a normalized form.
Both of these requirements are easily satisfied. The largest target
significand is 113 bits; we store at least 160. The smallest
denormal number fits in 17 exponent bits; we store 26.
Note that the decimal string conversion routines are sensitive to
rounding errors. Since the raw arithmetic routines do not themselves
have guard digits or rounding, the computation of 10**exp can
accumulate more than a few digits of error. The previous incarnation
of real.c successfully used a 144-bit fraction; given the current
layout of REAL_VALUE_TYPE we're forced to expand to at least 160 bits.
There is a bunch of fold_convert_* functions, e.g.:
fold_convert_const_int_from_int
fold_convert_const_int_from_real
fold_convert_const_int_from_fixed
fold_convert_const_real_from_real
and so on.
No comments:
Post a Comment