diff --git a/src/expression.d b/src/expression.d index 4f5471301f1d..62de31f86ce1 100644 --- a/src/expression.d +++ b/src/expression.d @@ -5200,6 +5200,7 @@ public: Expressions* keys; Expressions* values; OwnedBy ownedByCtfe = OWNEDcode; + Symbol* sym; extern (D) this(Loc loc, Expressions* keys, Expressions* values) { diff --git a/src/expression.h b/src/expression.h index 01a0d08e25c8..5464a4859522 100644 --- a/src/expression.h +++ b/src/expression.h @@ -440,6 +440,7 @@ class AssocArrayLiteralExp : public Expression Expressions *keys; Expressions *values; OwnedBy ownedByCtfe; + Symbol *sym; AssocArrayLiteralExp(Loc loc, Expressions *keys, Expressions *values); bool equals(RootObject *o); diff --git a/src/tocsym.c b/src/tocsym.c index b8dc5097df93..e65637f8faa3 100644 --- a/src/tocsym.c +++ b/src/tocsym.c @@ -48,6 +48,7 @@ typedef Array Symbols; Classsym *fake_classsym(Identifier *id); type *Type_toCtype(Type *t); void ClassReferenceExp_toInstanceDt(ClassReferenceExp *ce, DtBuilder* dtb); +void AssocArrayLiteralExp_toDt(AssocArrayLiteralExp *aale, DtBuilder* dtb); void Expression_toDt(Expression *e, DtBuilder* dtb); void cpp_type_info_ptr_toDt(ClassDeclaration *cd, DtBuilder* dtb); Symbol *toInitializer(AggregateDeclaration *ad); @@ -683,6 +684,24 @@ Symbol* toSymbol(StructLiteralExp *sle) return sle->sym; } +Symbol* toSymbol(AssocArrayLiteralExp *aale) +{ + if (aale->sym) return aale->sym; + TYPE *t = type_alloc(TYint); + t->Tcount++; + Symbol *s = symbol_calloc("internal"); + s->Sclass = SCstatic; + s->Sfl = FLextern; + s->Sflags |= SFLnodebug; + s->Stype = t; + aale->sym = s; + DtBuilder dtb; + AssocArrayLiteralExp_toDt(aale, &dtb); + s->Sdt = dtb.finish(); + outdata(s); + return aale->sym; +} + Symbol* toSymbol(ClassReferenceExp *cre) { if (cre->value->sym) return cre->value->sym; diff --git a/src/todt.d b/src/todt.d index fea0f16b13ae..3c7d7a84145e 100644 --- a/src/todt.d +++ b/src/todt.d @@ -54,6 +54,7 @@ extern (C++) void toObjFile(Dsymbol ds, bool multiobj); extern (C++) Symbol* toVtblSymbol(ClassDeclaration cd); extern (C++) Symbol* toSymbol(StructLiteralExp sle); extern (C++) Symbol* toSymbol(ClassReferenceExp cre); +extern (C++) Symbol* toSymbol(AssocArrayLiteralExp aale); extern (C++) Symbol* toInitializer(AggregateDeclaration ad); extern (C++) Symbol* toInitializer(EnumDeclaration ed); extern (C++) FuncDeclaration search_toString(StructDeclaration sd); @@ -568,6 +569,30 @@ extern (C++) void Expression_toDt(Expression e, DtBuilder dtb) ClassReferenceExp_toDt(e, dtb, 0); } + override void visit(AssocArrayLiteralExp aale) + { + assert(e.type.ty == Taarray); + TypeAArray taa = cast(TypeAArray)e.type; + + switch (taa.index.ty) + { + case Tchar: + case Twchar: + case Tdchar: + case Tint8: + case Tuns8: + case Tint16: + case Tuns16: + case Tint32: + case Tuns32: + dtb.xoff(toSymbol(aale), 0); + return; + default: + visit(cast(Expression)aale); + return; + } + } + override void visit(TypeidExp e) { if (Type t = isType(e.obj)) @@ -818,6 +843,173 @@ private void membersToDt(AggregateDeclaration ad, DtBuilder dtb, dtb.nzeros(ad.structsize - offset); } +private dinteger_t talign(dinteger_t tsize, dinteger_t algn) +{ + immutable mask = algn - 1; + assert(!(mask & algn)); + return (tsize + mask) & ~mask; +} + +private void aligntsizeExp(DtBuilder dtb, Expression e) +{ + Expression_toDt(e, dtb); + uint diff = cast(uint)e.type.size() & (Target.ptrsize - 1); + if (diff) + dtb.nzeros(Target.ptrsize - diff); +} + +private uinteger_t mix(uinteger_t v) +{ + enum m = 0x5bd1e995; + if (Target.ptrsize == 4) + { + // final mix function of MurmurHash2 + uint h = cast(uint)v; + h ^= h >> 13; + h *= m; + h ^= h >> 15; + return h; + } + else if (Target.ptrsize == 8) + { + // final mix function of MurmurHash2 + ulong h = v; + h ^= h >> 13; + h *= m; + h ^= h >> 15; + return h; + } + else + { + assert(0); + } +} + +private uinteger_t calcHash(Expression e) +{ + auto HASH_FILLED_MARK = uinteger_t(1) << 8 * Target.ptrsize - 1; + uinteger_t hash = e.toUInteger(); + return mix(hash) | HASH_FILLED_MARK; +} + +extern (C++) void AssocArrayLiteralExp_toDt(AssocArrayLiteralExp aale, DtBuilder dtb) +{ + //printf("AssocArrayLiteralExp::toDt(), this='%s'\n", aale.toChars()); + + assert(aale.type.ty == Taarray); + TypeAArray taa = cast(TypeAArray)aale.type; + + enum GROW_NUM = 4; + enum GROW_DEN = 5; + enum GROW_FAC = 4; + enum INIT_NUM_BUCKETS = 8; + + uint size = INIT_NUM_BUCKETS; + while (aale.keys.dim * GROW_DEN > size * GROW_NUM) + size *= GROW_FAC; + + uint firstUsedBucket = size; + auto mask = size - 1; + auto keysz = taa.index.size(); + auto valsz = taa.next.size(); + auto valoff = talign(taa.index.size(), taa.next.alignsize()); + auto esize = talign(valoff + keysz, Target.ptrsize); + + Array!size_t buckets; + buckets.setDim(size); + buckets.zero(); + + Dts entries; + entries.setDim(size); + entries.zero(); + + // Assign buckets and emit entries + for (size_t i = 0; i < aale.keys.dim; i++) + { + Expression ekey = (*aale.keys)[i]; + Expression evalue = (*aale.values)[i]; + + /* + struct Entry + { + Key key; + Value value; + } + */ + scope dtbe = new DtBuilder(); + dtbe.aligntsizeExp(ekey); + dtbe.aligntsizeExp(evalue); + + uinteger_t key_hash = calcHash(ekey); + for (size_t j = 0; j < size; j++) + { + size_t bucket = cast(size_t)((key_hash + j) & mask); + if (!entries[bucket]) + { + buckets[bucket] = cast(size_t)i; + entries[bucket] = dtbe.finish(); + break; + } + } + } + + scope dtbb = new DtBuilder(); + + // Generate buckets + for (size_t i = 0; i < size; i++) + { + /* + struct Bucket + { + size_t hash; + Entry* ptr; + } + */ + + if (entries[i]) + { + Expression ekey = (*aale.keys)[buckets[i]]; + uinteger_t key_hash = calcHash(ekey); + + dtbb.size(key_hash); + dtbb.dtoff(entries[i], 0); + + if (firstUsedBucket == size) + firstUsedBucket = i; + } + else + { + dtbb.size(0); + dtbb.size(0); + } + } + + /* + struct Impl + { + Bucket[] buckets; + uint used; + uint deleted; + TypeInfo_Struct entryTI; + uint firstUsed; + immutable uint keysz; + immutable uint valsz; + immutable uint valoff; + Flags flags; + } + */ + + dtb.size(size); // buckets.length + dtb.dtoff(dtbb.finish(), 0); // buckets.ptr + dtb.dword(aale.keys.dim); // used + dtb.dword(0); // deleted + dtb.size(0); // entryTI + dtb.dword(firstUsedBucket); + dtb.dword(cast(uint)keysz); + dtb.dword(cast(uint)valsz); + dtb.dword(cast(uint)valoff); + dtb.dword(0); // flags +} /* ================================================================= */ diff --git a/test/runnable/testaa4.d b/test/runnable/testaa4.d new file mode 100644 index 000000000000..2203f8cceca4 --- /dev/null +++ b/test/runnable/testaa4.d @@ -0,0 +1,42 @@ + +auto makeAA() +{ + return + [ + 1 : 2, + 2 : 3, + 3 : 4, + 4 : 5, + 5 : 6, + 6 : 7, + 7 : 8, + 8 : 9, + 9 : 10, + ]; +} + + +struct testAA(T, U) +{ + static void testAA(T key, U value)() + { + static a = [key : value]; + auto b = [key : value]; + assert(a == b); + } +} + +alias TT(T...) = T; + +void main() +{ + static a = makeAA(); + auto b = makeAA(); + + assert(a == b); + + foreach(V; TT!(char, wchar, dchar, byte, ubyte, short, ushort, int, uint)) + { + testAA!(int, V).testAA!(1, 1)(); + } +}