Showing with 319 additions and 92 deletions.
  1. +4 −4 src/ctfe.h
  2. +45 −8 src/ctfeexpr.c
  3. +4 −4 src/interpret.c
  4. +113 −76 src/target.c
  5. +45 −0 test/compilable/interpret3.d
  6. +108 −0 test/compilable/test13974.d
8 changes: 4 additions & 4 deletions src/ctfe.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,11 @@ UnionExp pointerArithmetic(Loc loc, TOK op, Type *type,
Expression *eptr, Expression *e2);

// True if conversion from type 'from' to 'to' involves a reinterpret_cast
// floating point -> integer or integer -> floating point
bool isFloatIntPaint(Type *to, Type *from);
// that can be handled by CTFE.
bool isCtfePaintable(Type *to, Type *from);

// Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
Expression *paintFloatInt(Expression *fromVal, Type *to);
// Reinterpret CTFE'd expresion 'fromVal' as type 'to'.
Expression *paintCtfeExpr(Expression *fromVal, Type *to);

/// Return true if t is an AA
bool isAssocArray(Type *t);
Expand Down
53 changes: 45 additions & 8 deletions src/ctfeexpr.c
Original file line number Diff line number Diff line change
Expand Up @@ -885,21 +885,58 @@ int comparePointers(Loc loc, TOK op, Type *type, Expression *agg1, dinteger_t of
}

// True if conversion from type 'from' to 'to' involves a reinterpret_cast
// floating point -> integer or integer -> floating point
bool isFloatIntPaint(Type *to, Type *from)
// that can be handled by CTFE.
bool isCtfePaintable(Type *to, Type *from)
{
return from->size() == to->size() &&
(from->isintegral() && to->isfloating() ||
from->isfloating() && to->isintegral());
// *&e is not a reinterpret cast.
if (from == to)
return false;

// Don't handle reinterpret casts of differing sizes.
if (from->size() != to->size())
return false;

// Don't handle anything larger than 512-bits.
if (from->size() > 64)
return false;

// Don't handle any cast to/from a non-scalar or static array type.
if (!from->isintegral() && !from->isfloating() && from->ty != Tsarray &&
!to->isintegral() && !to->isfloating() && to->ty != Tsarray)
return false;

// Don't handle casts from a non-scalar static array, or T[1]->T casts.
if (from->ty == Tsarray)
{
Type *tnext = from->nextOf();
if (!tnext->isintegral() && !tnext->isfloating())
return false;

if (tnext == to)
return false;
}

// Don't handle casts to a non-scalar static array, or T->T[1] casts.
if (to->ty == Tsarray)
{
Type *tnext = to->nextOf();
if (!tnext->isintegral() && !tnext->isfloating())
return false;

if (tnext == from)
return false;
}

return true;
}

// Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
Expression *paintFloatInt(Expression *fromVal, Type *to)
// Reinterpret CTFE'd expresion 'fromVal' as type 'to'.
Expression *paintCtfeExpr(Expression *fromVal, Type *to)
{
if (exceptionOrCantInterpret(fromVal))
return fromVal;

assert(to->size() == 4 || to->size() == 8);
assert(fromVal->type->size() == to->size());
return Target::paintAsType(fromVal, to);
}

Expand Down
8 changes: 4 additions & 4 deletions src/interpret.c
Original file line number Diff line number Diff line change
Expand Up @@ -5896,19 +5896,19 @@ class Interpreter : public Visitor
// Check for int<->float and long<->double casts.
if (e->e1->op == TOKsymoff && ((SymOffExp *)e->e1)->offset == 0 &&
((SymOffExp *)e->e1)->var->isVarDeclaration() &&
isFloatIntPaint(e->type, ((SymOffExp *)e->e1)->var->type))
isCtfePaintable(e->type, ((SymOffExp *)e->e1)->var->type))
{
// *(cast(int*)&v, where v is a float variable
result = paintFloatInt(getVarExp(e->loc, istate, ((SymOffExp *)e->e1)->var, ctfeNeedRvalue), e->type);
result = paintCtfeExpr(getVarExp(e->loc, istate, ((SymOffExp *)e->e1)->var, ctfeNeedRvalue), e->type);
return;
}
if (e->e1->op == TOKcast && ((CastExp *)e->e1)->e1->op == TOKaddress)
{
// *(cast(int *))&x where x is a float expression
Expression *x = ((AddrExp *)(((CastExp *)e->e1)->e1))->e1;
if (isFloatIntPaint(e->type, x->type))
if (isCtfePaintable(e->type, x->type))
{
result = paintFloatInt(interpret(x, istate), e->type);
result = paintCtfeExpr(interpret(x, istate), e->type);
return;
}
}
Expand Down
189 changes: 113 additions & 76 deletions src/target.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,40 +206,30 @@ Type *Target::va_listType()
* Private helpers for Target::paintAsType.
*/

// Write the integer value of 'e' into a unsigned byte buffer.
static void encodeInteger(Expression *e, unsigned char *buffer)
{
dinteger_t value = e->toInteger();
int size = (int)e->type->size();

for (int p = 0; p < size; p++)
{
int offset = p; // Would be (size - 1) - p; on BigEndian
buffer[offset] = ((value >> (p * 8)) & 0xFF);
}
}

// Write the bytes encoded in 'buffer' into an integer and returns
// the value as a new IntegerExp.
static Expression *decodeInteger(Loc loc, Type *type, unsigned char *buffer)
{
dinteger_t value = 0;
int size = (int)type->size();

for (int p = 0; p < size; p++)
{
int offset = p; // Would be (size - 1) - p; on BigEndian
value |= ((dinteger_t)buffer[offset] << (p * 8));
}

return new IntegerExp(loc, value, type);
}

// Write the real value of 'e' into a unsigned byte buffer.
static void encodeReal(Expression *e, unsigned char *buffer)
// Write the Expression value of 'e' into a unsigned byte buffer.
static void encodeExpression(Expression *e, unsigned char *buffer)
{
switch (e->type->ty)
{
case Tint8:
case Tuns8:
case Tint16:
case Tuns16:
case Tint32:
case Tuns32:
case Tint64:
case Tuns64:
{
dinteger_t value = e->toInteger();
size_t size = e->type->size();

for (int p = 0; p < size; p++)
{
int offset = p; // Would be (size - 1) - p; on BigEndian
buffer[offset] = ((value >> (p * 8)) & 0xFF);
}
break;
}
case Tfloat32:
{
float *p = (float *)buffer;
Expand All @@ -252,36 +242,115 @@ static void encodeReal(Expression *e, unsigned char *buffer)
*p = (double)e->toReal();
break;
}
case Tsarray:
{
size_t esize = e->type->nextOf()->size();
assert(e->op == TOKarrayliteral);
Expressions *elems = ((ArrayLiteralExp *) e)->elements;

// Encode each element into the buffer.
for (size_t i = 0; i < elems->dim; i++)
{
Expression *elem = (*elems)[i];
encodeExpression(elem, buffer + (esize * i));
}
break;
}
case Tvector:
{
VectorExp *ve = (VectorExp *)e;
if (ve->e1->op == TOKarrayliteral)
{
// Encode underlying static array.
encodeExpression(ve->e1, buffer);
}
else
{
// Construct from single value.
TypeVector *vtype = ((TypeVector *)ve->type);
size_t esize = vtype->elementType()->size(Loc());
size_t dims = ((TypeSArray *)vtype->basetype)->dim->toInteger();

for (size_t i = 0; i < dims; i++)
{
encodeExpression(ve->e1, buffer + (esize * i));
}
}
break;
}
default:
assert(0);
}
}

// Write the bytes encoded in 'buffer' into a longdouble and returns
// the value as a new RealExp.
static Expression *decodeReal(Loc loc, Type *type, unsigned char *buffer)
// Write the bytes encoded in 'buffer' into a native value and returns
// the value as a new Expression.
static Expression *decodeExpression(Loc loc, Type *type, unsigned char *buffer)
{
longdouble value;

switch (type->ty)
{
case Tint8:
case Tuns8:
case Tint16:
case Tuns16:
case Tint32:
case Tuns32:
case Tint64:
case Tuns64:
{
dinteger_t value = 0;
size_t size = type->size();

for (int p = 0; p < size; p++)
{
int offset = p; // Would be (size - 1) - p; on BigEndian
value |= ((dinteger_t)buffer[offset] << (p * 8));
}
return new IntegerExp(loc, value, type);
}
case Tfloat32:
{
float *p = (float *)buffer;
value = ldouble(*p);
break;
longdouble value = ldouble(*p);
return new RealExp(loc, value, type);
}
case Tfloat64:
{
double *p = (double *)buffer;
value = ldouble(*p);
break;
longdouble value = ldouble(*p);
return new RealExp(loc, value, type);
}
case Tsarray:
{
Type *etype = type->nextOf();
size_t esize = etype->size();
size_t dims = ((TypeSArray *)type)->dim->toInteger();

// Decode each offset of the buffer as the element type.
Expressions *elems = new Expressions();
elems->setDim(dims);
for (size_t i = 0; i < dims; i++)
(*elems)[i] = decodeExpression(loc, etype, buffer + (esize * i));

Expression *e = new ArrayLiteralExp(loc, elems);
e->type = type;
return e;
}
case Tvector:
{
// Decode underlying static array, and return as vector.
Type *atype = ((TypeVector *)type)->basetype;
Expression *e = decodeExpression(loc, atype, buffer);
e = new VectorExp(loc, e, type);
e->type = type;
return e;
}
default:
{
assert(0);
return NULL; // avoid warning
}
}

return new RealExp(loc, value, type);
}

/******************************
Expand All @@ -299,42 +368,10 @@ Expression *Target::paintAsType(Expression *e, Type *type)
assert(e->type->size() == type->size());

// Write the expression into the buffer.
switch (e->type->ty)
{
case Tint32:
case Tuns32:
case Tint64:
case Tuns64:
encodeInteger(e, buffer);
break;

case Tfloat32:
case Tfloat64:
encodeReal(e, buffer);
break;

default:
assert(0);
}
encodeExpression(e, buffer);

// Interpret the buffer as a new type.
switch (type->ty)
{
case Tint32:
case Tuns32:
case Tint64:
case Tuns64:
return decodeInteger(e->loc, type, buffer);

case Tfloat32:
case Tfloat64:
return decodeReal(e->loc, type, buffer);

default:
assert(0);
}

return NULL; // avoid warning
return decodeExpression(e->loc, type, buffer);
}

/*
Expand Down
45 changes: 45 additions & 0 deletions test/compilable/interpret3.d
Original file line number Diff line number Diff line change
Expand Up @@ -4043,6 +4043,51 @@ bool bug9170()

static assert(bug9170());

/**************************************************
13974 Allow reinterpret casts between
scalar and static arrays.
**************************************************/

short[4] cast13974a(int[2] x) {
return *(cast(short[4]*)&x);
}

long cast13974b(short[4] x) {
return *(cast(long*)&x);
}

int[2] cast13974c(long x) {
return *(cast(int[2]*)&x);
}

byte[8] cast13974e(long x) {
return *(cast(byte[8]*)&x);
}

double cast13974f(byte[8] x) {
return *(cast(double*)&x);
}

byte[8] cast13974g(double x) {
return *(cast(byte[8]*)&x);
}

bool bug13974()
{
int[2] l2arr = [1, 5];
short[4] s4arr = cast13974a(l2arr);
long lint = cast13974b(s4arr);
assert(cast13974c(lint) == l2arr);

byte[8] b8arr = cast13974e(lint);
double dfloat = cast13974f(b8arr);
assert(cast13974g(dfloat) == b8arr);

return true;
}

static assert(bug13974());

/**************************************************
6792 ICE with pointer cast of indexed array
**************************************************/
Expand Down
Loading