Files
ldc/dmd2/arrayop.c
David Nadlinger cb341586e3 First merge of 2.064 beta.
This corresponds to DMD commit a913ce4bc59a94a022a27e390fc841f4aededffb.

Doesn't build Phobos yet.
2013-10-29 19:21:15 +01:00

772 lines
22 KiB
C

// Copyright (c) 1999-2012 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "rmem.h"
#include "aav.h"
#include "expression.h"
#include "statement.h"
#include "mtype.h"
#include "declaration.h"
#include "scope.h"
#include "id.h"
#include "module.h"
#include "init.h"
#if IN_DMD
extern int binary(const char *p , const char **tab, int high);
/**************************************
* Hash table of array op functions already generated or known about.
*/
AA *arrayfuncs;
#else
int binary(const char *p , const char **tab, int high)
{
int i = 0, j = high, k, l;
do
{
k = (i + j) / 2;
l = strcmp(p, tab[k]);
if (!l)
return k;
else if (l < 0)
j = k;
else
i = k + 1;
}
while (i != j);
return -1;
}
#endif
/**************************************
* Structure to contain information needed to insert an array op call
*/
struct ArrayOp
{
FuncDeclaration *cFunc; // Stub for optimized druntime version
FuncDeclaration *dFunc; // Full D version for ctfe
};
/**************************************
* Search for a druntime array op
*/
int isDruntimeArrayOp(Identifier *ident)
{
/* Some of the array op functions are written as library functions,
* presumably to optimize them with special CPU vector instructions.
* List those library functions here, in alpha order.
*/
static const char *libArrayopFuncs[] =
{
"_arrayExpSliceAddass_a",
"_arrayExpSliceAddass_d", // T[]+=T
"_arrayExpSliceAddass_f", // T[]+=T
"_arrayExpSliceAddass_g",
"_arrayExpSliceAddass_h",
"_arrayExpSliceAddass_i",
"_arrayExpSliceAddass_k",
"_arrayExpSliceAddass_s",
"_arrayExpSliceAddass_t",
"_arrayExpSliceAddass_u",
"_arrayExpSliceAddass_w",
"_arrayExpSliceDivass_d", // T[]/=T
"_arrayExpSliceDivass_f", // T[]/=T
"_arrayExpSliceMinSliceAssign_a",
"_arrayExpSliceMinSliceAssign_d", // T[]=T-T[]
"_arrayExpSliceMinSliceAssign_f", // T[]=T-T[]
"_arrayExpSliceMinSliceAssign_g",
"_arrayExpSliceMinSliceAssign_h",
"_arrayExpSliceMinSliceAssign_i",
"_arrayExpSliceMinSliceAssign_k",
"_arrayExpSliceMinSliceAssign_s",
"_arrayExpSliceMinSliceAssign_t",
"_arrayExpSliceMinSliceAssign_u",
"_arrayExpSliceMinSliceAssign_w",
"_arrayExpSliceMinass_a",
"_arrayExpSliceMinass_d", // T[]-=T
"_arrayExpSliceMinass_f", // T[]-=T
"_arrayExpSliceMinass_g",
"_arrayExpSliceMinass_h",
"_arrayExpSliceMinass_i",
"_arrayExpSliceMinass_k",
"_arrayExpSliceMinass_s",
"_arrayExpSliceMinass_t",
"_arrayExpSliceMinass_u",
"_arrayExpSliceMinass_w",
"_arrayExpSliceMulass_d", // T[]*=T
"_arrayExpSliceMulass_f", // T[]*=T
"_arrayExpSliceMulass_i",
"_arrayExpSliceMulass_k",
"_arrayExpSliceMulass_s",
"_arrayExpSliceMulass_t",
"_arrayExpSliceMulass_u",
"_arrayExpSliceMulass_w",
"_arraySliceExpAddSliceAssign_a",
"_arraySliceExpAddSliceAssign_d", // T[]=T[]+T
"_arraySliceExpAddSliceAssign_f", // T[]=T[]+T
"_arraySliceExpAddSliceAssign_g",
"_arraySliceExpAddSliceAssign_h",
"_arraySliceExpAddSliceAssign_i",
"_arraySliceExpAddSliceAssign_k",
"_arraySliceExpAddSliceAssign_s",
"_arraySliceExpAddSliceAssign_t",
"_arraySliceExpAddSliceAssign_u",
"_arraySliceExpAddSliceAssign_w",
"_arraySliceExpDivSliceAssign_d", // T[]=T[]/T
"_arraySliceExpDivSliceAssign_f", // T[]=T[]/T
"_arraySliceExpMinSliceAssign_a",
"_arraySliceExpMinSliceAssign_d", // T[]=T[]-T
"_arraySliceExpMinSliceAssign_f", // T[]=T[]-T
"_arraySliceExpMinSliceAssign_g",
"_arraySliceExpMinSliceAssign_h",
"_arraySliceExpMinSliceAssign_i",
"_arraySliceExpMinSliceAssign_k",
"_arraySliceExpMinSliceAssign_s",
"_arraySliceExpMinSliceAssign_t",
"_arraySliceExpMinSliceAssign_u",
"_arraySliceExpMinSliceAssign_w",
"_arraySliceExpMulSliceAddass_d", // T[] += T[]*T
"_arraySliceExpMulSliceAddass_f",
"_arraySliceExpMulSliceAddass_r",
"_arraySliceExpMulSliceAssign_d", // T[]=T[]*T
"_arraySliceExpMulSliceAssign_f", // T[]=T[]*T
"_arraySliceExpMulSliceAssign_i",
"_arraySliceExpMulSliceAssign_k",
"_arraySliceExpMulSliceAssign_s",
"_arraySliceExpMulSliceAssign_t",
"_arraySliceExpMulSliceAssign_u",
"_arraySliceExpMulSliceAssign_w",
"_arraySliceExpMulSliceMinass_d", // T[] -= T[]*T
"_arraySliceExpMulSliceMinass_f",
"_arraySliceExpMulSliceMinass_r",
"_arraySliceSliceAddSliceAssign_a",
"_arraySliceSliceAddSliceAssign_d", // T[]=T[]+T[]
"_arraySliceSliceAddSliceAssign_f", // T[]=T[]+T[]
"_arraySliceSliceAddSliceAssign_g",
"_arraySliceSliceAddSliceAssign_h",
"_arraySliceSliceAddSliceAssign_i",
"_arraySliceSliceAddSliceAssign_k",
"_arraySliceSliceAddSliceAssign_r", // T[]=T[]+T[]
"_arraySliceSliceAddSliceAssign_s",
"_arraySliceSliceAddSliceAssign_t",
"_arraySliceSliceAddSliceAssign_u",
"_arraySliceSliceAddSliceAssign_w",
"_arraySliceSliceAddass_a",
"_arraySliceSliceAddass_d", // T[]+=T[]
"_arraySliceSliceAddass_f", // T[]+=T[]
"_arraySliceSliceAddass_g",
"_arraySliceSliceAddass_h",
"_arraySliceSliceAddass_i",
"_arraySliceSliceAddass_k",
"_arraySliceSliceAddass_s",
"_arraySliceSliceAddass_t",
"_arraySliceSliceAddass_u",
"_arraySliceSliceAddass_w",
"_arraySliceSliceMinSliceAssign_a",
"_arraySliceSliceMinSliceAssign_d", // T[]=T[]-T[]
"_arraySliceSliceMinSliceAssign_f", // T[]=T[]-T[]
"_arraySliceSliceMinSliceAssign_g",
"_arraySliceSliceMinSliceAssign_h",
"_arraySliceSliceMinSliceAssign_i",
"_arraySliceSliceMinSliceAssign_k",
"_arraySliceSliceMinSliceAssign_r", // T[]=T[]-T[]
"_arraySliceSliceMinSliceAssign_s",
"_arraySliceSliceMinSliceAssign_t",
"_arraySliceSliceMinSliceAssign_u",
"_arraySliceSliceMinSliceAssign_w",
"_arraySliceSliceMinass_a",
"_arraySliceSliceMinass_d", // T[]-=T[]
"_arraySliceSliceMinass_f", // T[]-=T[]
"_arraySliceSliceMinass_g",
"_arraySliceSliceMinass_h",
"_arraySliceSliceMinass_i",
"_arraySliceSliceMinass_k",
"_arraySliceSliceMinass_s",
"_arraySliceSliceMinass_t",
"_arraySliceSliceMinass_u",
"_arraySliceSliceMinass_w",
"_arraySliceSliceMulSliceAssign_d", // T[]=T[]*T[]
"_arraySliceSliceMulSliceAssign_f", // T[]=T[]*T[]
"_arraySliceSliceMulSliceAssign_i",
"_arraySliceSliceMulSliceAssign_k",
"_arraySliceSliceMulSliceAssign_s",
"_arraySliceSliceMulSliceAssign_t",
"_arraySliceSliceMulSliceAssign_u",
"_arraySliceSliceMulSliceAssign_w",
"_arraySliceSliceMulass_d", // T[]*=T[]
"_arraySliceSliceMulass_f", // T[]*=T[]
"_arraySliceSliceMulass_i",
"_arraySliceSliceMulass_k",
"_arraySliceSliceMulass_s",
"_arraySliceSliceMulass_t",
"_arraySliceSliceMulass_u",
"_arraySliceSliceMulass_w",
};
char *name = ident->toChars();
int i = binary(name, libArrayopFuncs, sizeof(libArrayopFuncs) / sizeof(char *));
if (i != -1)
return 1;
#ifdef DEBUG // Make sure our array is alphabetized
for (i = 0; i < sizeof(libArrayopFuncs) / sizeof(char *); i++)
{
if (strcmp(name, libArrayopFuncs[i]) == 0)
assert(0);
}
#endif
return 0;
}
ArrayOp *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc, Loc loc)
{
Parameters *fparams = new Parameters();
Expression *loopbody = exp->buildArrayLoop(fparams);
ArrayOp *op = new ArrayOp;
if (isDruntimeArrayOp(ident))
#if IN_LLVM
{
#endif
op->cFunc = FuncDeclaration::genCfunc(fparams, exp->type, ident);
#if IN_LLVM
op->cFunc->isArrayOp = 2;
}
#endif
else
op->cFunc = NULL;
/* Construct the function body:
* foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++)
* loopbody;
* return p;
*/
Parameter *p = (*fparams)[0 /*fparams->dim - 1*/];
#if DMDV1
// for (size_t i = 0; i < p.length; i++)
Initializer *init = new ExpInitializer(0, new IntegerExp(0, 0, Type::tsize_t));
Dsymbol *d = new VarDeclaration(0, Type::tsize_t, Id::p, init);
Statement *s1 = new ForStatement(0,
new ExpStatement(0, d),
new CmpExp(TOKlt, 0, new IdentifierExp(0, Id::p), new ArrayLengthExp(0, new IdentifierExp(0, p->ident))),
new PostExp(TOKplusplus, 0, new IdentifierExp(0, Id::p)),
new ExpStatement(0, loopbody));
#else
// foreach (i; 0 .. p.length)
Statement *s1 = new ForeachRangeStatement(Loc(), TOKforeach,
new Parameter(0, NULL, Id::p, NULL),
new IntegerExp(Loc(), 0, Type::tsize_t),
new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p->ident)),
new ExpStatement(Loc(), loopbody));
#endif
//printf("%s\n", s1->toChars());
Statement *s2 = new ReturnStatement(Loc(), new IdentifierExp(Loc(), p->ident));
//printf("s2: %s\n", s2->toChars());
Statement *fbody = new CompoundStatement(Loc(), s1, s2);
// Built-in array ops should be @trusted, pure and nothrow
StorageClass stc = STCtrusted | STCpure | STCnothrow;
/* Construct the function
*/
TypeFunction *ftype = new TypeFunction(fparams, exp->type, 0, LINKc, stc);
//printf("ftype: %s\n", ftype->toChars());
FuncDeclaration *fd = new FuncDeclaration(Loc(), Loc(), ident, STCundefined, ftype);
fd->fbody = fbody;
fd->protection = PROTpublic;
fd->linkage = LINKc;
fd->isArrayOp = 1;
if (!op->cFunc)
sc->module->importedFrom->members->push(fd);
sc = sc->push();
sc->parent = sc->module->importedFrom;
sc->stc = 0;
sc->linkage = LINKc;
fd->semantic(sc);
fd->semantic2(sc);
fd->semantic3(sc);
sc->pop();
if (op->cFunc)
{
op->cFunc->dArrayOp = fd;
op->cFunc->type = fd->type;
}
op->dFunc = fd;
return op;
}
/**********************************************
* Check that there are no uses of arrays without [].
*/
bool isArrayOpValid(Expression *e)
{
if (e->op == TOKslice)
return true;
if (e->op == TOKarrayliteral)
{
Type *t = e->type->toBasetype();
while (t->ty == Tarray || t->ty == Tsarray)
t = t->nextOf()->toBasetype();
return (t->ty != Tvoid);
}
Type *tb = e->type->toBasetype();
if ( (tb->ty == Tarray) || (tb->ty == Tsarray) )
{
switch (e->op)
{
case TOKadd:
case TOKmin:
case TOKmul:
case TOKdiv:
case TOKmod:
case TOKxor:
case TOKand:
case TOKor:
case TOKassign:
case TOKaddass:
case TOKminass:
case TOKmulass:
case TOKdivass:
case TOKmodass:
case TOKxorass:
case TOKandass:
case TOKorass:
#if DMDV2
case TOKpow:
case TOKpowass:
#endif
return isArrayOpValid(((BinExp *)e)->e1) && isArrayOpValid(((BinExp *)e)->e2);
case TOKcall:
return false; // TODO: Decide if [] is required after arrayop calls.
case TOKneg:
case TOKtilde:
return isArrayOpValid(((UnaExp *)e)->e1);
default:
return false;
}
}
return true;
}
/***********************************
* Construct the array operation expression.
*/
Expression *BinExp::arrayOp(Scope *sc)
{
//printf("BinExp::arrayOp() %s\n", toChars());
Type *tb = type->toBasetype();
assert(tb->ty == Tarray || tb->ty == Tsarray);
if (tb->nextOf()->toBasetype()->ty == Tvoid)
{
error("Cannot perform array operations on void[] arrays");
return new ErrorExp();
}
if (!isArrayOpValid(e2))
{
e2->error("invalid array operation %s (did you forget a [] ?)", toChars());
return new ErrorExp();
}
Expressions *arguments = new Expressions();
/* The expression to generate an array operation for is mangled
* into a name to use as the array operation function name.
* Mangle in the operands and operators in RPN order, and type.
*/
OutBuffer buf;
buf.writestring("_array");
buildArrayIdent(&buf, arguments);
buf.writeByte('_');
/* Append deco of array element type
*/
#if DMDV2
buf.writestring(type->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco);
#else
buf.writestring(type->toBasetype()->nextOf()->toBasetype()->deco);
#endif
buf.writeByte(0);
char *name = buf.toChars();
Identifier *ident = Lexer::idPool(name);
#if IN_LLVM
ArrayOp **pOp = (ArrayOp **)_aaGet(&sc->module->arrayfuncs, ident);
#else
ArrayOp **pOp = (ArrayOp **)_aaGet(&arrayfuncs, ident);
#endif
ArrayOp *op = *pOp;
if (!op)
op = buildArrayOp(ident, this, sc, loc);
*pOp = op;
FuncDeclaration *fd = op->cFunc ? op->cFunc : op->dFunc;
Expression *ec = new VarExp(loc, fd);
Expression *e = new CallExp(loc, ec, arguments);
return e->semantic(sc);
}
Expression *BinAssignExp::arrayOp(Scope *sc)
{
//printf("BinAssignExp::arrayOp() %s\n", toChars());
/* Check that the elements of e1 can be assigned to
*/
Type *tn = e1->type->toBasetype()->nextOf();
if (tn && (!tn->isMutable() || !tn->isAssignable()))
{
error("slice %s is not mutable", e1->toChars());
return new ErrorExp();
}
return BinExp::arrayOp(sc);
}
/******************************************
* Construct the identifier for the array operation function,
* and build the argument list to pass to it.
*/
void Expression::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
{
buf->writestring("Exp");
arguments->shift(this);
}
void CastExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
{
Type *tb = type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
e1->buildArrayIdent(buf, arguments);
}
else
Expression::buildArrayIdent(buf, arguments);
}
void ArrayLiteralExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
{
buf->writestring("Slice");
arguments->shift(this);
}
void SliceExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
{
buf->writestring("Slice");
arguments->shift(this);
}
void AssignExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
{
/* Evaluate assign expressions right to left
*/
e2->buildArrayIdent(buf, arguments);
e1->buildArrayIdent(buf, arguments);
buf->writestring("Assign");
}
void BinAssignExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
{
/* Evaluate assign expressions right to left
*/
e2->buildArrayIdent(buf, arguments);
e1->buildArrayIdent(buf, arguments);
const char *s;
switch(op)
{
case TOKaddass: s = "Addass"; break;
case TOKminass: s = "Subass"; break;
case TOKmulass: s = "Mulass"; break;
case TOKdivass: s = "Divass"; break;
case TOKmodass: s = "Modass"; break;
case TOKxorass: s = "Xorass"; break;
case TOKandass: s = "Andass"; break;
case TOKorass: s = "Orass"; break;
#if DMDV2
case TOKpowass: s = "Powass"; break;
#endif
default: assert(0);
}
buf->writestring(s);
}
void NegExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
{
e1->buildArrayIdent(buf, arguments);
buf->writestring("Neg");
}
void ComExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
{
e1->buildArrayIdent(buf, arguments);
buf->writestring("Com");
}
void BinExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments)
{
/* Evaluate assign expressions left to right
*/
const char *s = NULL;
switch(op)
{
case TOKadd: s = "Add"; break;
case TOKmin: s = "Sub"; break;
case TOKmul: s = "Mul"; break;
case TOKdiv: s = "Div"; break;
case TOKmod: s = "Mod"; break;
case TOKxor: s = "Xor"; break;
case TOKand: s = "And"; break;
case TOKor: s = "Or"; break;
#if DMDV2
case TOKpow: s = "Pow"; break;
#endif
default: break;
}
if (s)
{
e1->buildArrayIdent(buf, arguments);
e2->buildArrayIdent(buf, arguments);
buf->writestring(s);
}
else
Expression::buildArrayIdent(buf, arguments);
}
/******************************************
* Construct the inner loop for the array operation function,
* and build the parameter list.
*/
Expression *Expression::buildArrayLoop(Parameters *fparams)
{
Identifier *id = Identifier::generateId("c", fparams->dim);
Parameter *param = new Parameter(0, type, id, NULL);
fparams->shift(param);
Expression *e = new IdentifierExp(Loc(), id);
return e;
}
Expression *CastExp::buildArrayLoop(Parameters *fparams)
{
Type *tb = type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
return e1->buildArrayLoop(fparams);
}
else
return Expression::buildArrayLoop(fparams);
}
Expression *ArrayLiteralExp::buildArrayLoop(Parameters *fparams)
{
Identifier *id = Identifier::generateId("p", fparams->dim);
Parameter *param = new Parameter(STCconst, type, id, NULL);
fparams->shift(param);
Expression *e = new IdentifierExp(Loc(), id);
Expressions *arguments = new Expressions();
Expression *index = new IdentifierExp(Loc(), Id::p);
arguments->push(index);
e = new ArrayExp(Loc(), e, arguments);
return e;
}
Expression *SliceExp::buildArrayLoop(Parameters *fparams)
{
Identifier *id = Identifier::generateId("p", fparams->dim);
Parameter *param = new Parameter(STCconst, type, id, NULL);
fparams->shift(param);
Expression *e = new IdentifierExp(Loc(), id);
Expressions *arguments = new Expressions();
Expression *index = new IdentifierExp(Loc(), Id::p);
arguments->push(index);
e = new ArrayExp(Loc(), e, arguments);
return e;
}
Expression *AssignExp::buildArrayLoop(Parameters *fparams)
{
/* Evaluate assign expressions right to left
*/
Expression *ex2 = e2->buildArrayLoop(fparams);
#if DMDV2
/* Need the cast because:
* b = c + p[i];
* where b is a byte fails because (c + p[i]) is an int
* which cannot be implicitly cast to byte.
*/
ex2 = new CastExp(Loc(), ex2, e1->type->nextOf());
#endif
Expression *ex1 = e1->buildArrayLoop(fparams);
Parameter *param = (*fparams)[0];
param->storageClass = 0;
Expression *e = new AssignExp(Loc(), ex1, ex2);
return e;
}
Expression *BinAssignExp::buildArrayLoop(Parameters *fparams)
{
/* Evaluate assign expressions right to left
*/
Expression *ex2 = e2->buildArrayLoop(fparams);
Expression *ex1 = e1->buildArrayLoop(fparams);
Parameter *param = (*fparams)[0];
param->storageClass = 0;
Expression *e;
switch(op)
{
case TOKaddass: return new AddAssignExp(loc, ex1, ex2);
case TOKminass: return new MinAssignExp(loc, ex1, ex2);
case TOKmulass: return new MulAssignExp(loc, ex1, ex2);
case TOKdivass: return new DivAssignExp(loc, ex1, ex2);
case TOKmodass: return new ModAssignExp(loc, ex1, ex2);
case TOKxorass: return new XorAssignExp(loc, ex1, ex2);
case TOKandass: return new AndAssignExp(loc, ex1, ex2);
case TOKorass: return new OrAssignExp(loc, ex1, ex2);
#if DMDV2
case TOKpowass: return new PowAssignExp(loc, ex1, ex2);
#endif
default:
assert(0);
return NULL;
}
}
Expression *NegExp::buildArrayLoop(Parameters *fparams)
{
Expression *ex1 = e1->buildArrayLoop(fparams);
Expression *e = new NegExp(Loc(), ex1);
return e;
}
Expression *ComExp::buildArrayLoop(Parameters *fparams)
{
Expression *ex1 = e1->buildArrayLoop(fparams);
Expression *e = new ComExp(Loc(), ex1);
return e;
}
Expression *BinExp::buildArrayLoop(Parameters *fparams)
{
switch(op)
{
case TOKadd:
case TOKmin:
case TOKmul:
case TOKdiv:
case TOKmod:
case TOKxor:
case TOKand:
case TOKor:
#if DMDV2
case TOKpow:
#endif
{
/* Evaluate assign expressions left to right
*/
BinExp *e = (BinExp *)copy();
e->e1 = e->e1->buildArrayLoop(fparams);
e->e2 = e->e2->buildArrayLoop(fparams);
e->type = NULL;
return e;
}
default:
return Expression::buildArrayLoop(fparams);
}
}
/***********************************************
* Test if operand is a valid array op operand.
*/
int Expression::isArrayOperand()
{
//printf("Expression::isArrayOperand() %s\n", toChars());
if (op == TOKslice)
return 1;
if (op == TOKarrayliteral)
{
Type *t = type->toBasetype();
while (t->ty == Tarray || t->ty == Tsarray)
t = t->nextOf()->toBasetype();
return (t->ty != Tvoid);
}
if (type->toBasetype()->ty == Tarray)
{
switch (op)
{
case TOKadd:
case TOKmin:
case TOKmul:
case TOKdiv:
case TOKmod:
case TOKxor:
case TOKand:
case TOKor:
case TOKassign:
case TOKaddass:
case TOKminass:
case TOKmulass:
case TOKdivass:
case TOKmodass:
case TOKxorass:
case TOKandass:
case TOKorass:
#if DMDV2
case TOKpow:
case TOKpowass:
#endif
case TOKneg:
case TOKtilde:
return 1;
default:
break;
}
}
return 0;
}