[svn r92] Fixed support for statically initialized unions. lots of bugfixes as cleanups too.

This commit is contained in:
Tomas Lindquist Olsen 2007-11-06 10:03:14 +01:00
parent 3b2cb94f6e
commit 34d9e12020
16 changed files with 849 additions and 275 deletions

View file

@ -45,6 +45,8 @@ namespace llvm
class ConstantStruct; class ConstantStruct;
} }
struct DUnion;
struct AggregateDeclaration : ScopeDsymbol struct AggregateDeclaration : ScopeDsymbol
{ {
Type *type; Type *type;
@ -104,6 +106,7 @@ struct AggregateDeclaration : ScopeDsymbol
llvm::ConstantStruct* llvmConstVtbl; llvm::ConstantStruct* llvmConstVtbl;
llvm::Constant* llvmInitZ; llvm::Constant* llvmInitZ;
bool llvmHasUnions; bool llvmHasUnions;
DUnion* llvmUnion;
AggregateDeclaration *isAggregateDeclaration() { return this; } AggregateDeclaration *isAggregateDeclaration() { return this; }
}; };

View file

@ -51,6 +51,7 @@ AggregateDeclaration::AggregateDeclaration(Loc loc, Identifier *id)
llvmInitZ = NULL; llvmInitZ = NULL;
llvmInProgress = false; llvmInProgress = false;
llvmHasUnions = false; llvmHasUnions = false;
llvmUnion = NULL;
} }
enum PROT AggregateDeclaration::prot() enum PROT AggregateDeclaration::prot()

View file

@ -251,41 +251,59 @@ void DtoSetArray(llvm::Value* arr, llvm::Value* dim, llvm::Value* ptr)
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
llvm::Constant* DtoConstArrayInitializer(ArrayInitializer* arrinit) llvm::Constant* DtoConstArrayInitializer(ArrayInitializer* arrinit)
{ {
Logger::println("arr init begin"); Logger::println("DtoConstArrayInitializer: %s | %s", arrinit->toChars(), arrinit->type->toChars());
LOG_SCOPE;
Type* arrinittype = DtoDType(arrinit->type); Type* arrinittype = DtoDType(arrinit->type);
Type* t; Type* t;
integer_t tdim; integer_t tdim;
if (arrinittype->ty == Tsarray) { if (arrinittype->ty == Tsarray) {
Logger::println("static array");
TypeSArray* tsa = (TypeSArray*)arrinittype; TypeSArray* tsa = (TypeSArray*)arrinittype;
tdim = tsa->dim->toInteger(); tdim = tsa->dim->toInteger();
t = tsa; t = tsa;
} }
else if (arrinittype->ty == Tarray) { else if (arrinittype->ty == Tarray) {
Logger::println("dynamic array");
t = arrinittype; t = arrinittype;
tdim = arrinit->dim; tdim = arrinit->dim;
} }
else else
assert(0); assert(0);
std::vector<llvm::Constant*> inits(tdim, 0); Logger::println("dim = %u", tdim);
std::vector<llvm::Constant*> inits(tdim, NULL);
const llvm::Type* elemty = DtoType(arrinittype->next); const llvm::Type* elemty = DtoType(arrinittype->next);
assert(arrinit->index.dim == arrinit->value.dim); assert(arrinit->index.dim == arrinit->value.dim);
for (int i=0,j=0; i < tdim; ++i) for (unsigned i=0,j=0; i < tdim; ++i)
{ {
Initializer* init = 0; Initializer* init = 0;
Expression* idx = (Expression*)arrinit->index.data[j]; Expression* idx = (Expression*)arrinit->index.data[j];
if (idx) if (idx)
{ {
integer_t k = idx->toInteger(); // this is pretty weird :/ idx->type turned out NULL for the initializer:
if (i == k) // const in6_addr IN6ADDR_ANY = { s6_addr8: [0] };
{ // in std.c.linux.socket
init = (Initializer*)arrinit->value.data[j]; if (idx->type) {
assert(init); //integer_t k = idx->toInteger();
++j; Logger::println("getting value for exp: %s | %s", idx->toChars(), idx->type->toChars());
llvm::Constant* cc = idx->toConstElem(gIR);
Logger::println("value gotten");
assert(cc != NULL);
llvm::ConstantInt* ci = llvm::dyn_cast<llvm::ConstantInt>(cc);
assert(ci != NULL);
uint64_t k = ci->getZExtValue();
if (i == k)
{
init = (Initializer*)arrinit->value.data[j];
assert(init);
++j;
}
} }
} }
else else
@ -294,29 +312,7 @@ llvm::Constant* DtoConstArrayInitializer(ArrayInitializer* arrinit)
++j; ++j;
} }
llvm::Constant* v = 0; llvm::Constant* v = DtoConstInitializer(t->next, init);
if (!init)
{
v = t->next->defaultInit()->toConstElem(gIR);
}
else if (ExpInitializer* ex = init->isExpInitializer())
{
v = ex->exp->toConstElem(gIR);
}
else if (StructInitializer* si = init->isStructInitializer())
{
v = DtoConstStructInitializer(si);
}
else if (ArrayInitializer* ai = init->isArrayInitializer())
{
v = DtoConstArrayInitializer(ai);
}
else if (init->isVoidInitializer())
{
v = llvm::UndefValue::get(elemty);
}
else
assert(v); assert(v);
inits[i] = v; inits[i] = v;

101
gen/dvalue.cpp Normal file
View file

@ -0,0 +1,101 @@
#include "gen/llvm.h"
#include "declaration.h"
#include "gen/tollvm.h"
#include "gen/irstate.h"
#include "gen/logger.h"
#include "gen/dvalue.h"
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
DVarValue::DVarValue(VarDeclaration* vd, llvm::Value* llvmValue, bool lvalue)
{
var = vd;
val = llvmValue;
rval = 0;
lval = lvalue;
type = var->type;
}
DVarValue::DVarValue(Type* t, llvm::Value* lv, llvm::Value* rv)
{
var = 0;
val = lv;
rval = rv;
lval = true;
type = t;
}
DVarValue::DVarValue(Type* t, llvm::Value* llvmValue, bool lvalue)
{
var = 0;
val = llvmValue;
rval = 0;
lval = lvalue;
type = t;
}
llvm::Value* DVarValue::getLVal()
{
assert(val && lval);
return val;
}
llvm::Value* DVarValue::getRVal()
{
assert(rval || val);
if (DtoIsPassedByRef(type)) {
if (rval) return rval;
return val;
}
else {
if (rval) return rval;
Logger::cout() << "val: " << *val << '\n';
if (llvm::isa<llvm::Argument>(val)) {
if (var && (var->isRef() || var->isOut()))
return DtoLoad(val);
}
else if (!isField() && DtoCanLoad(val)) {
return DtoLoad(val);
}
return val;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
DFuncValue::DFuncValue(FuncDeclaration* fd, llvm::Value* v, llvm::Value* vt)
{
func = fd;
type = func->type;
val = v;
vthis = vt;
cc = (unsigned)-1;
}
llvm::Value* DFuncValue::getLVal()
{
assert(0);
return 0;
}
llvm::Value* DFuncValue::getRVal()
{
assert(val);
return val;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
llvm::Value* DConstValue::getRVal()
{
assert(c);
return c;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////

193
gen/dvalue.h Normal file
View file

@ -0,0 +1,193 @@
#ifndef LLVMDC_GEN_DVALUE_H
#define LLVMDC_GEN_DVALUE_H
/*
These classes are used for generating the IR. They encapsulate D values and
provide a common interface to the most common operations. When more specialized
handling is necessary, they hold enough information to do-the-right-thing (TM)
*/
#include <cassert>
#include "root.h"
struct Type;
struct Dsymbol;
struct VarDeclaration;
struct FuncDeclaration;
namespace llvm
{
class Value;
class Type;
class Constant;
}
struct DImValue;
struct DConstValue;
struct DNullValue;
struct DVarValue;
struct DFieldValue;
struct DThisValue;
struct DFuncValue;
struct DSliceValue;
struct DArrayLenValue;
struct DLValueCast;
// base class for d-values
struct DValue : Object
{
virtual Type* getType() = 0;
virtual llvm::Value* getLVal() { assert(0); return 0; }
virtual llvm::Value* getRVal() { assert(0); return 0; }
virtual DImValue* isIm() { return NULL; }
virtual DConstValue* isConst() { return NULL; }
virtual DNullValue* isNull() { return NULL; }
virtual DVarValue* isVar() { return NULL; }
virtual DFieldValue* isField() { return NULL; }
virtual DThisValue* isThis() { return NULL; }
virtual DSliceValue* isSlice() { return NULL; }
virtual DFuncValue* isFunc() { return NULL; }
virtual DArrayLenValue* isArrayLen() { return NULL; }
virtual DLValueCast* isLValueCast() { return NULL; }
virtual bool inPlace() { return false; }
protected:
DValue() {}
DValue(const DValue&) { }
DValue& operator=(const DValue&) { return *this; }
};
// immediate d-value
struct DImValue : DValue
{
Type* type;
llvm::Value* val;
bool inplace;
DImValue(Type* t, llvm::Value* v, bool i = false) { type = t; val = v; inplace = i; }
virtual llvm::Value* getRVal() { assert(val); return val; }
virtual Type* getType() { assert(type); return type; }
virtual DImValue* isIm() { return this; }
virtual bool inPlace() { return inplace; }
};
// constant d-value
struct DConstValue : DValue
{
Type* type;
llvm::Constant* c;
DConstValue(Type* t, llvm::Constant* con) { type = t; c = con; }
virtual llvm::Value* getRVal();
virtual Type* getType() { assert(type); return type; }
virtual DConstValue* isConst() { return this; }
};
// null d-value
struct DNullValue : DConstValue
{
DNullValue(Type* t, llvm::Constant* con) : DConstValue(t,con) {}
virtual DNullValue* isNull() { return this; }
};
// variable d-value
struct DVarValue : DValue
{
Type* type;
VarDeclaration* var;
llvm::Value* val;
llvm::Value* rval;
bool lval;
DVarValue(VarDeclaration* vd, llvm::Value* llvmValue, bool lvalue);
DVarValue(Type* vd, llvm::Value* lv, llvm::Value* rv);
DVarValue(Type* t, llvm::Value* llvmValue, bool lvalue);
virtual llvm::Value* getLVal();
virtual llvm::Value* getRVal();
virtual Type* getType() { assert(type); return type; }
virtual DVarValue* isVar() { return this; }
};
// field d-value
struct DFieldValue : DVarValue
{
DFieldValue(Type* t, llvm::Value* llvmValue, bool l) : DVarValue(t, llvmValue, l) {}
virtual DFieldValue* isField() { return this; }
};
// this d-value
struct DThisValue : DVarValue
{
DThisValue(VarDeclaration* vd, llvm::Value* llvmValue) : DVarValue(vd, llvmValue, true) {}
virtual DThisValue* isThis() { return this; }
};
// array length d-value
struct DArrayLenValue : DVarValue
{
DArrayLenValue(Type* t, llvm::Value* llvmValue) : DVarValue(t, llvmValue, true) {}
virtual DArrayLenValue* isArrayLen() { return this; }
};
// slice d-value
struct DSliceValue : DValue
{
Type* type;
llvm::Value* len;
llvm::Value* ptr;
DSliceValue(Type* t, llvm::Value* l, llvm::Value* p) { type=t; ptr=p; len=l; }
virtual Type* getType() { assert(type); return type; }
virtual DSliceValue* isSlice() { return this; }
};
// function d-value
struct DFuncValue : DValue
{
Type* type;
FuncDeclaration* func;
llvm::Value* val;
llvm::Value* vthis;
unsigned cc;
DFuncValue(FuncDeclaration* fd, llvm::Value* v, llvm::Value* vt = 0);
virtual llvm::Value* getLVal();
virtual llvm::Value* getRVal();
virtual Type* getType() { assert(type); return type; }
virtual DFuncValue* isFunc() { return this; }
};
// l-value cast d-value
struct DLValueCast : DValue
{
Type* type;
llvm::Value* lval;
llvm::Value* rval;
DLValueCast(Type* t, llvm::Value* l, llvm::Value* r) {
type = t;
lval = l;
rval = r;
}
virtual llvm::Value* getLVal() { assert(lval); return lval; }
virtual llvm::Value* getRVal() { assert(rval); return rval; }
virtual Type* getType() { assert(type); return type; }
virtual DLValueCast* isLValueCast() { return this; }
};
#endif // LLVMDC_GEN_DVALUE_H

319
gen/structs.cpp Normal file
View file

@ -0,0 +1,319 @@
#include <algorithm>
#include "gen/llvm.h"
#include "mtype.h"
#include "aggregate.h"
#include "init.h"
#include "declaration.h"
#include "gen/irstate.h"
#include "gen/tollvm.h"
#include "gen/arrays.h"
#include "gen/logger.h"
#include "gen/structs.h"
//////////////////////////////////////////////////////////////////////////////////////////
const llvm::Type* DtoStructType(Type* t)
{
assert(0);
std::vector<const llvm::Type*> types;
return llvm::StructType::get(types);
}
//////////////////////////////////////////////////////////////////////////////////////////
llvm::Value* DtoStructZeroInit(llvm::Value* v)
{
assert(gIR);
uint64_t n = gTargetData->getTypeSize(v->getType()->getContainedType(0));
//llvm::Type* sarrty = llvm::PointerType::get(llvm::ArrayType::get(llvm::Type::Int8Ty, n));
llvm::Type* sarrty = llvm::PointerType::get(llvm::Type::Int8Ty);
llvm::Value* sarr = new llvm::BitCastInst(v,sarrty,"tmp",gIR->scopebb());
llvm::Function* fn = LLVM_DeclareMemSet32();
std::vector<llvm::Value*> llargs;
llargs.resize(4);
llargs[0] = sarr;
llargs[1] = llvm::ConstantInt::get(llvm::Type::Int8Ty, 0, false);
llargs[2] = llvm::ConstantInt::get(llvm::Type::Int32Ty, n, false);
llargs[3] = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0, false);
llvm::Value* ret = new llvm::CallInst(fn, llargs.begin(), llargs.end(), "", gIR->scopebb());
return ret;
}
//////////////////////////////////////////////////////////////////////////////////////////
llvm::Value* DtoStructCopy(llvm::Value* dst, llvm::Value* src)
{
Logger::cout() << "dst = " << *dst << " src = " << *src << '\n';
assert(dst->getType() == src->getType());
assert(gIR);
uint64_t n = gTargetData->getTypeSize(dst->getType()->getContainedType(0));
//llvm::Type* sarrty = llvm::PointerType::get(llvm::ArrayType::get(llvm::Type::Int8Ty, n));
llvm::Type* arrty = llvm::PointerType::get(llvm::Type::Int8Ty);
llvm::Value* dstarr = new llvm::BitCastInst(dst,arrty,"tmp",gIR->scopebb());
llvm::Value* srcarr = new llvm::BitCastInst(src,arrty,"tmp",gIR->scopebb());
llvm::Function* fn = LLVM_DeclareMemCpy32();
std::vector<llvm::Value*> llargs;
llargs.resize(4);
llargs[0] = dstarr;
llargs[1] = srcarr;
llargs[2] = llvm::ConstantInt::get(llvm::Type::Int32Ty, n, false);
llargs[3] = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0, false);
return new llvm::CallInst(fn, llargs.begin(), llargs.end(), "", gIR->scopebb());
}
//////////////////////////////////////////////////////////////////////////////////////////
llvm::Constant* DtoConstStructInitializer(StructInitializer* si)
{
Logger::println("DtoConstStructInitializer: %s", si->toChars());
LOG_SCOPE;
const llvm::StructType* structtype = llvm::cast<llvm::StructType>(si->ad->llvmType);
Logger::cout() << "llvm struct type: " << *structtype << '\n';
assert(si->value.dim == si->vars.dim);
std::vector<DUnionIdx> inits;
for (int i = 0; i < si->value.dim; ++i)
{
Initializer* ini = (Initializer*)si->value.data[i];
assert(ini);
VarDeclaration* vd = (VarDeclaration*)si->vars.data[i];
assert(vd);
llvm::Constant* v = DtoConstInitializer(vd->type, ini);
inits.push_back(DUnionIdx(vd->llvmFieldIndex, vd->llvmFieldIndexOffset, v));
}
return si->ad->llvmUnion->getConst(inits);
}
//////////////////////////////////////////////////////////////////////////////////////////
llvm::Value* DtoIndexStruct(llvm::Value* ptr, StructDeclaration* sd, Type* t, unsigned os, std::vector<unsigned>& idxs)
{
Logger::println("checking for offset %u type %s:", os, t->toChars());
LOG_SCOPE;
if (idxs.empty())
idxs.push_back(0);
const llvm::Type* llt = llvm::PointerType::get(DtoType(t));
const llvm::Type* st = llvm::PointerType::get(DtoType(sd->type));
if (ptr->getType() != st) {
assert(sd->llvmHasUnions);
ptr = gIR->ir->CreateBitCast(ptr, st, "tmp");
}
for (unsigned i=0; i<sd->fields.dim; ++i) {
VarDeclaration* vd = (VarDeclaration*)sd->fields.data[i];
Type* vdtype = DtoDType(vd->type);
Logger::println("found %u type %s", vd->offset, vdtype->toChars());
assert(vd->llvmFieldIndex >= 0);
if (os == vd->offset && vdtype == t) {
idxs.push_back(vd->llvmFieldIndex);
ptr = DtoGEP(ptr, idxs, "tmp");
if (ptr->getType() != llt)
ptr = gIR->ir->CreateBitCast(ptr, llt, "tmp");
if (vd->llvmFieldIndexOffset)
ptr = new llvm::GetElementPtrInst(ptr, DtoConstUint(vd->llvmFieldIndexOffset), "tmp", gIR->scopebb());
return ptr;
}
else if (vdtype->ty == Tstruct && (vd->offset + vdtype->size()) > os) {
TypeStruct* ts = (TypeStruct*)vdtype;
StructDeclaration* ssd = ts->sym;
idxs.push_back(vd->llvmFieldIndex);
if (vd->llvmFieldIndexOffset) {
Logger::println("has union field offset");
ptr = DtoGEP(ptr, idxs, "tmp");
if (ptr->getType() != llt)
ptr = gIR->ir->CreateBitCast(ptr, llt, "tmp");
ptr = new llvm::GetElementPtrInst(ptr, DtoConstUint(vd->llvmFieldIndexOffset), "tmp", gIR->scopebb());
std::vector<unsigned> tmp;
return DtoIndexStruct(ptr, ssd, t, os-vd->offset, tmp);
}
else {
const llvm::Type* sty = llvm::PointerType::get(DtoType(vd->type));
if (ptr->getType() != sty) {
ptr = gIR->ir->CreateBitCast(ptr, sty, "tmp");
std::vector<unsigned> tmp;
return DtoIndexStruct(ptr, ssd, t, os-vd->offset, tmp);
}
else {
return DtoIndexStruct(ptr, ssd, t, os-vd->offset, idxs);
}
}
}
}
size_t llt_sz = gTargetData->getTypeSize(llt->getContainedType(0));
assert(os % llt_sz == 0);
ptr = gIR->ir->CreateBitCast(ptr, llt, "tmp");
return new llvm::GetElementPtrInst(ptr, DtoConstUint(os / llt_sz), "tmp", gIR->scopebb());
}
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////// D UNION HELPER CLASS ////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
DUnion::DUnion()
{
DUnionField* f = NULL;
IRStruct& topstruct = gIR->topstruct();
bool unions = false;
for (IRStruct::OffsetMap::iterator i=topstruct.offsets.begin(); i!=topstruct.offsets.end(); ++i)
{
unsigned o = i->first;
IRStruct::Offset* so = &i->second;
const llvm::Type* ft = so->init->getType();
size_t sz = gTargetData->getTypeSize(ft);
if (f == NULL) { // new field
fields.push_back(DUnionField());
f = &fields.back();
f->size = sz;
f->offset = o;
f->init = so->init;
f->initsize = sz;
f->types.push_back(ft);
}
else if (o == f->offset) { // same offset
if (sz > f->size)
f->size = sz;
f->types.push_back(ft);
unions = true;
}
else if (o < f->offset+f->size) {
assert((o+sz) <= (f->offset+f->size));
unions = true;
}
else {
fields.push_back(DUnionField());
f = &fields.back();
f->size = sz;
f->offset = o;
f->init = so->init;
f->initsize = sz;
f->types.push_back(ft);
}
}
{
LOG_SCOPE;
Logger::println("******** DUnion BEGIN");
size_t n = fields.size();
for (size_t i=0; i<n; ++i) {
Logger::cout()<<"field #"<<i<<" offset: "<<fields[i].offset<<" size: "<<fields[i].size<<'('<<fields[i].initsize<<")\n";
LOG_SCOPE;
size_t nt = fields[i].types.size();
for (size_t j=0; j<nt; ++j) {
Logger::cout()<<*fields[i].types[j]<<'\n';
}
}
Logger::println("******** DUnion END");
}
}
static void push_nulls(size_t nbytes, std::vector<llvm::Constant*>& out)
{
assert(nbytes > 0);
std::vector<llvm::Constant*> i(nbytes, llvm::ConstantInt::get(llvm::Type::Int8Ty, 0, false));
out.push_back(llvm::ConstantArray::get(llvm::ArrayType::get(llvm::Type::Int8Ty, nbytes), i));
}
llvm::Constant* DUnion::getConst(std::vector<DUnionIdx>& in)
{
std::sort(in.begin(), in.end());
std::vector<llvm::Constant*> out;
size_t nin = in.size();
size_t nfields = fields.size();
size_t fi = 0;
size_t last = 0;
size_t ii = 0;
size_t os = 0;
for(;;)
{
if (fi == nfields) break;
bool nextSame = (ii+1 < nin) && (in[ii+1].idx == fi);
if (ii < nin && fi == in[ii].idx)
{
size_t s = gTargetData->getTypeSize(in[ii].c->getType());
if (in[ii].idx == last)
{
size_t nos = in[ii].idxos * s;
if (nos && nos-os) {
assert(nos >= os);
push_nulls(nos-os, out);
}
os = nos + s;
}
else
{
os = s;
}
out.push_back(in[ii].c);
ii++;
if (!nextSame)
{
if (os < fields[fi].size)
push_nulls(fields[fi].size - os, out);
os = 0;
last = fi++;
}
continue;
}
// default initialize if necessary
if (ii == nin || fi < in[ii].idx)
{
DUnionField& f = fields[fi];
out.push_back(f.init);
if (f.initsize < f.size)
push_nulls(f.size - f.initsize, out);
last = fi++;
os = 0;
continue;
}
}
std::vector<const llvm::Type*> tys;
size_t nout = out.size();
for (size_t i=0; i<nout; ++i)
tys.push_back(out[i]->getType());
const llvm::StructType* st = llvm::StructType::get(tys);
return llvm::ConstantStruct::get(st, out);
}

49
gen/structs.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef LLVMD_GEN_STRUCTS_H
#define LLVMD_GEN_STRUCTS_H
struct StructInitializer;
const llvm::Type* DtoStructType(Type* t);
llvm::Value* DtoStructZeroInit(llvm::Value* v);
llvm::Value* DtoStructCopy(llvm::Value* dst, llvm::Value* src);
llvm::Constant* DtoConstStructInitializer(StructInitializer* si);
llvm::Value* DtoIndexStruct(llvm::Value* ptr, StructDeclaration* sd, Type* t, unsigned os, std::vector<unsigned>& idxs);
struct DUnionField
{
unsigned offset;
size_t size;
std::vector<const llvm::Type*> types;
llvm::Constant* init;
size_t initsize;
DUnionField() {
offset = 0;
size = 0;
init = NULL;
initsize = 0;
}
};
struct DUnionIdx
{
unsigned idx,idxos;
llvm::Constant* c;
DUnionIdx()
: idx(0), c(0) {}
DUnionIdx(unsigned _idx, unsigned _idxos, llvm::Constant* _c)
: idx(_idx), idxos(_idxos), c(_c) {}
bool operator<(const DUnionIdx& i) const {
return (idx < i.idx) || (idx == i.idx && idxos < i.idxos);
}
};
class DUnion
{
std::vector<DUnionField> fields;
public:
DUnion();
llvm::Constant* getConst(std::vector<DUnionIdx>& in);
};
#endif

View file

@ -27,6 +27,7 @@
#include "gen/tollvm.h" #include "gen/tollvm.h"
#include "gen/runtime.h" #include "gen/runtime.h"
#include "gen/arrays.h" #include "gen/arrays.h"
#include "gen/structs.h"
#include "gen/dvalue.h" #include "gen/dvalue.h"
@ -178,9 +179,12 @@ DValue* VarExp::toElem(IRState* p)
} }
else { else {
// take care of forward references of global variables // take care of forward references of global variables
if (!vd->llvmTouched && vd->isDataseg()) if (!vd->llvmTouched && (vd->isDataseg() || (vd->storage_class & STCextern))) // !vd->onstack)
vd->toObjFile(); vd->toObjFile();
assert(vd->llvmValue); if (!vd->llvmValue) {
Logger::println("global variable not resolved :/ %s", vd->toChars());
assert(0);
}
return new DVarValue(vd, vd->llvmValue, true); return new DVarValue(vd, vd->llvmValue, true);
} }
} }
@ -225,7 +229,7 @@ llvm::Constant* VarExp::toConstElem(IRState* p)
assert(ts->sym->llvmInitZ); assert(ts->sym->llvmInitZ);
return ts->sym->llvmInitZ; return ts->sym->llvmInitZ;
} }
assert(0 && "Only support const var exp is SymbolDeclaration"); assert(0 && "Only supported const VarExp is of a SymbolDeclaration");
return NULL; return NULL;
} }
@ -247,14 +251,15 @@ llvm::Constant* IntegerExp::toConstElem(IRState* p)
LOG_SCOPE; LOG_SCOPE;
const llvm::Type* t = DtoType(type); const llvm::Type* t = DtoType(type);
if (llvm::isa<llvm::PointerType>(t)) { if (llvm::isa<llvm::PointerType>(t)) {
Logger::println("pointer");
llvm::Constant* i = llvm::ConstantInt::get(DtoSize_t(),(uint64_t)value,false); llvm::Constant* i = llvm::ConstantInt::get(DtoSize_t(),(uint64_t)value,false);
return llvm::ConstantExpr::getIntToPtr(i, t); return llvm::ConstantExpr::getIntToPtr(i, t);
} }
else if (llvm::isa<llvm::IntegerType>(t)) { assert(llvm::isa<llvm::IntegerType>(t));
return llvm::ConstantInt::get(t,(uint64_t)value,!type->isunsigned()); llvm::Constant* c = llvm::ConstantInt::get(t,(uint64_t)value,!type->isunsigned());
} assert(c);
assert(0); Logger::cout() << "value = " << *c << '\n';
return NULL; return c;
} }
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
@ -1447,7 +1452,7 @@ DValue* CastExp::toElem(IRState* p)
if (isslice) { if (isslice) {
return new DSliceValue(type, rval2, rval); return new DSliceValue(type, rval2, rval);
} }
else if (u->isLValueCast() || u->isVar()) { else if (u->isLValueCast() || (u->isVar() && u->isVar()->lval)) {
return new DLValueCast(type, u->getLVal(), rval); return new DLValueCast(type, u->getLVal(), rval);
} }
else if (p->topexp() && p->topexp()->e1 == this) { else if (p->topexp() && p->topexp()->e1 == this) {
@ -2875,7 +2880,7 @@ STUB(RemoveExp);
STUB(AssocArrayLiteralExp); STUB(AssocArrayLiteralExp);
//STUB(StructLiteralExp); //STUB(StructLiteralExp);
#define CONSTSTUB(x) llvm::Constant* x::toConstElem(IRState * p) {error("const Exp type "#x" not implemented: '%s' type: '%s'", toChars(), type->toChars()); assert(0); fatal(); return NULL; } #define CONSTSTUB(x) llvm::Constant* x::toConstElem(IRState * p) {error("const Exp type "#x" not implemented: '%s' type: '%s'", toChars(), type->toChars()); fatal(); return NULL; }
CONSTSTUB(Expression); CONSTSTUB(Expression);
//CONSTSTUB(IntegerExp); //CONSTSTUB(IntegerExp);
//CONSTSTUB(RealExp); //CONSTSTUB(RealExp);

View file

@ -14,6 +14,7 @@
#include "gen/runtime.h" #include "gen/runtime.h"
#include "gen/arrays.h" #include "gen/arrays.h"
#include "gen/dvalue.h" #include "gen/dvalue.h"
#include "gen/structs.h"
bool DtoIsPassedByRef(Type* type) bool DtoIsPassedByRef(Type* type)
{ {
@ -373,16 +374,6 @@ const llvm::StructType* DtoDelegateType(Type* t)
return llvm::StructType::get(types); return llvm::StructType::get(types);
} }
//////////////////////////////////////////////////////////////////////////////////////////
const llvm::Type* DtoStructType(Type* t)
{
assert(0);
std::vector<const llvm::Type*> types;
return llvm::StructType::get(types);
}
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
static llvm::Function* LLVM_DeclareMemIntrinsic(const char* name, int bits, bool set=false) static llvm::Function* LLVM_DeclareMemIntrinsic(const char* name, int bits, bool set=false)
@ -454,132 +445,6 @@ llvm::Function* LLVM_DeclareMemCpy64()
return _func; return _func;
} }
//////////////////////////////////////////////////////////////////////////////////////////
llvm::Value* DtoStructZeroInit(llvm::Value* v)
{
assert(gIR);
uint64_t n = gTargetData->getTypeSize(v->getType()->getContainedType(0));
//llvm::Type* sarrty = llvm::PointerType::get(llvm::ArrayType::get(llvm::Type::Int8Ty, n));
llvm::Type* sarrty = llvm::PointerType::get(llvm::Type::Int8Ty);
llvm::Value* sarr = new llvm::BitCastInst(v,sarrty,"tmp",gIR->scopebb());
llvm::Function* fn = LLVM_DeclareMemSet32();
std::vector<llvm::Value*> llargs;
llargs.resize(4);
llargs[0] = sarr;
llargs[1] = llvm::ConstantInt::get(llvm::Type::Int8Ty, 0, false);
llargs[2] = llvm::ConstantInt::get(llvm::Type::Int32Ty, n, false);
llargs[3] = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0, false);
llvm::Value* ret = new llvm::CallInst(fn, llargs.begin(), llargs.end(), "", gIR->scopebb());
return ret;
}
//////////////////////////////////////////////////////////////////////////////////////////
llvm::Value* DtoStructCopy(llvm::Value* dst, llvm::Value* src)
{
Logger::cout() << "dst = " << *dst << " src = " << *src << '\n';
assert(dst->getType() == src->getType());
assert(gIR);
uint64_t n = gTargetData->getTypeSize(dst->getType()->getContainedType(0));
//llvm::Type* sarrty = llvm::PointerType::get(llvm::ArrayType::get(llvm::Type::Int8Ty, n));
llvm::Type* arrty = llvm::PointerType::get(llvm::Type::Int8Ty);
llvm::Value* dstarr = new llvm::BitCastInst(dst,arrty,"tmp",gIR->scopebb());
llvm::Value* srcarr = new llvm::BitCastInst(src,arrty,"tmp",gIR->scopebb());
llvm::Function* fn = LLVM_DeclareMemCpy32();
std::vector<llvm::Value*> llargs;
llargs.resize(4);
llargs[0] = dstarr;
llargs[1] = srcarr;
llargs[2] = llvm::ConstantInt::get(llvm::Type::Int32Ty, n, false);
llargs[3] = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0, false);
return new llvm::CallInst(fn, llargs.begin(), llargs.end(), "", gIR->scopebb());
}
//////////////////////////////////////////////////////////////////////////////////////////
llvm::Constant* DtoConstStructInitializer(StructInitializer* si)
{
llvm::StructType* structtype = llvm::cast<llvm::StructType>(si->ad->llvmType);
size_t n = structtype->getNumElements();
assert(si->value.dim == si->vars.dim);
std::vector<llvm::Constant*> inits;
inits.resize(n, NULL);
for (int i = 0; i < si->value.dim; ++i)
{
Initializer* ini = (Initializer*)si->value.data[i];
assert(ini);
VarDeclaration* vd = (VarDeclaration*)si->vars.data[i];
Type* vdtype = DtoDType(vd->type);
assert(vd);
Logger::println("vars[%d] = %s", i, vd->toChars());
llvm::Constant* v = 0;
assert(vd->llvmFieldIndex >= 0);
unsigned idx = vd->llvmFieldIndex;
if (ExpInitializer* ex = ini->isExpInitializer())
{
v = ex->exp->toConstElem(gIR);
}
else if (StructInitializer* si = ini->isStructInitializer())
{
v = DtoConstStructInitializer(si);
}
else if (ArrayInitializer* ai = ini->isArrayInitializer())
{
v = DtoConstArrayInitializer(ai);
}
else if (ini->isVoidInitializer())
{
v = llvm::UndefValue::get(structtype->getElementType(idx));
}
else
assert(v);
inits[idx] = v;
Logger::cout() << "init[" << idx << "] = " << *v << '\n';
}
// fill out nulls
assert(si->ad->llvmInitZ);
if (si->ad->llvmInitZ->isNullValue())
{
for (int i = 0; i < n; ++i)
{
if (inits[i] == 0)
{
inits[i] = llvm::Constant::getNullValue(structtype->getElementType(i));
}
}
}
else
{
for (int i = 0; i < n; ++i)
{
if (inits[i] == 0)
{
inits[i] = si->ad->llvmInitZ->getOperand(i);
}
}
}
return llvm::ConstantStruct::get(structtype, inits);
}
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
llvm::Value* DtoNullDelegate(llvm::Value* v) llvm::Value* DtoNullDelegate(llvm::Value* v)
@ -1495,65 +1360,6 @@ llvm::Value* DtoBitCast(llvm::Value* v, const llvm::Type* t)
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
llvm::Value* DtoIndexStruct(llvm::Value* ptr, StructDeclaration* sd, Type* t, unsigned os, std::vector<unsigned>& idxs)
{
Logger::println("checking for offset %u type %s:", os, t->toChars());
LOG_SCOPE;
if (idxs.empty())
idxs.push_back(0);
const llvm::Type* llt = llvm::PointerType::get(DtoType(t));
for (unsigned i=0; i<sd->fields.dim; ++i) {
VarDeclaration* vd = (VarDeclaration*)sd->fields.data[i];
Type* vdtype = DtoDType(vd->type);
Logger::println("found %u type %s", vd->offset, vdtype->toChars());
assert(vd->llvmFieldIndex >= 0);
if (os == vd->offset && vdtype == t) {
idxs.push_back(vd->llvmFieldIndex);
ptr = DtoGEP(ptr, idxs, "tmp");
if (ptr->getType() != llt)
ptr = gIR->ir->CreateBitCast(ptr, llt, "tmp");
if (vd->llvmFieldIndexOffset)
ptr = new llvm::GetElementPtrInst(ptr, DtoConstUint(vd->llvmFieldIndexOffset), "tmp", gIR->scopebb());
return ptr;
}
else if (vdtype->ty == Tstruct && (vd->offset + vdtype->size()) > os) {
TypeStruct* ts = (TypeStruct*)vdtype;
StructDeclaration* ssd = ts->sym;
idxs.push_back(vd->llvmFieldIndex);
if (vd->llvmFieldIndexOffset) {
Logger::println("has union field offset");
ptr = DtoGEP(ptr, idxs, "tmp");
if (ptr->getType() != llt)
ptr = gIR->ir->CreateBitCast(ptr, llt, "tmp");
ptr = new llvm::GetElementPtrInst(ptr, DtoConstUint(vd->llvmFieldIndexOffset), "tmp", gIR->scopebb());
std::vector<unsigned> tmp;
return DtoIndexStruct(ptr, ssd, t, os-vd->offset, tmp);
}
else {
const llvm::Type* sty = llvm::PointerType::get(DtoType(vd->type));
if (ptr->getType() != sty) {
ptr = gIR->ir->CreateBitCast(ptr, sty, "tmp");
std::vector<unsigned> tmp;
return DtoIndexStruct(ptr, ssd, t, os-vd->offset, tmp);
}
else {
return DtoIndexStruct(ptr, ssd, t, os-vd->offset, idxs);
}
}
}
}
size_t llt_sz = gTargetData->getTypeSize(llt->getContainedType(0));
assert(os % llt_sz == 0);
ptr = gIR->ir->CreateBitCast(ptr, llt, "tmp");
return new llvm::GetElementPtrInst(ptr, DtoConstUint(os / llt_sz), "tmp", gIR->scopebb());
}
//////////////////////////////////////////////////////////////////////////////////////////
bool DtoIsTemplateInstance(Dsymbol* s) bool DtoIsTemplateInstance(Dsymbol* s)
{ {
if (!s) return false; if (!s) return false;
@ -1563,3 +1369,28 @@ bool DtoIsTemplateInstance(Dsymbol* s)
return DtoIsTemplateInstance(s->parent); return DtoIsTemplateInstance(s->parent);
return false; return false;
} }
void DtoLazyStaticInit(bool istempl, llvm::Value* gvar, Initializer* init, Type* t)
{
// create a flag to make sure initialization only happens once
llvm::GlobalValue::LinkageTypes gflaglink = istempl ? llvm::GlobalValue::WeakLinkage : llvm::GlobalValue::InternalLinkage;
std::string gflagname(gvar->getName());
gflagname.append("__initflag");
llvm::GlobalVariable* gflag = new llvm::GlobalVariable(llvm::Type::Int1Ty,false,gflaglink,DtoConstBool(false),gflagname,gIR->module);
// check flag and do init if not already done
llvm::BasicBlock* oldend = gIR->scopeend();
llvm::BasicBlock* initbb = new llvm::BasicBlock("ifnotinit",gIR->topfunc(),oldend);
llvm::BasicBlock* endinitbb = new llvm::BasicBlock("ifnotinitend",gIR->topfunc(),oldend);
llvm::Value* cond = gIR->ir->CreateICmpEQ(gIR->ir->CreateLoad(gflag,"tmp"),DtoConstBool(false));
gIR->ir->CreateCondBr(cond, initbb, endinitbb);
gIR->scope() = IRScope(initbb,endinitbb);
DValue* ie = DtoInitializer(init);
if (!ie->inPlace()) {
DValue* dst = new DVarValue(t, gvar, true);
DtoAssign(dst, ie);
}
gIR->ir->CreateStore(DtoConstBool(true), gflag);
gIR->ir->CreateBr(endinitbb);
gIR->scope() = IRScope(endinitbb,oldend);
}

View file

@ -3,18 +3,12 @@
// D -> LLVM helpers // D -> LLVM helpers
struct StructInitializer;
struct DValue; struct DValue;
const llvm::Type* DtoType(Type* t); const llvm::Type* DtoType(Type* t);
bool DtoIsPassedByRef(Type* type); bool DtoIsPassedByRef(Type* type);
Type* DtoDType(Type* t); Type* DtoDType(Type* t);
const llvm::Type* DtoStructType(Type* t);
llvm::Value* DtoStructZeroInit(llvm::Value* v);
llvm::Value* DtoStructCopy(llvm::Value* dst, llvm::Value* src);
llvm::Constant* DtoConstStructInitializer(StructInitializer* si);
const llvm::FunctionType* DtoFunctionType(Type* t, const llvm::Type* thistype, bool ismain = false); const llvm::FunctionType* DtoFunctionType(Type* t, const llvm::Type* thistype, bool ismain = false);
const llvm::FunctionType* DtoFunctionType(FuncDeclaration* fdecl); const llvm::FunctionType* DtoFunctionType(FuncDeclaration* fdecl);
llvm::Function* DtoDeclareFunction(FuncDeclaration* fdecl); llvm::Function* DtoDeclareFunction(FuncDeclaration* fdecl);
@ -66,10 +60,10 @@ llvm::Constant* DtoConstString(const char*);
llvm::Constant* DtoConstStringPtr(const char* str, const char* section = 0); llvm::Constant* DtoConstStringPtr(const char* str, const char* section = 0);
llvm::Constant* DtoConstBool(bool); llvm::Constant* DtoConstBool(bool);
llvm::Value* DtoIndexStruct(llvm::Value* ptr, StructDeclaration* sd, Type* t, unsigned os, std::vector<unsigned>& idxs);
bool DtoIsTemplateInstance(Dsymbol* s); bool DtoIsTemplateInstance(Dsymbol* s);
void DtoLazyStaticInit(bool istempl, llvm::Value* gvar, Initializer* init, Type* t);
// llvm wrappers // llvm wrappers
void DtoMemCpy(llvm::Value* dst, llvm::Value* src, llvm::Value* nbytes); void DtoMemCpy(llvm::Value* dst, llvm::Value* src, llvm::Value* nbytes);
bool DtoCanLoad(llvm::Value* ptr); bool DtoCanLoad(llvm::Value* ptr);

View file

@ -36,6 +36,7 @@
#include "gen/logger.h" #include "gen/logger.h"
#include "gen/tollvm.h" #include "gen/tollvm.h"
#include "gen/arrays.h" #include "gen/arrays.h"
#include "gen/structs.h"
#include "gen/todebug.h" #include "gen/todebug.h"
#include "gen/runtime.h" #include "gen/runtime.h"
@ -287,6 +288,8 @@ void StructDeclaration::toObjFile()
gIR->module->addTypeName(mangle(),ts->llvmType); gIR->module->addTypeName(mangle(),ts->llvmType);
} }
llvmUnion = new DUnion; // uses gIR->topstruct()
// generate static data // generate static data
llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::ExternalLinkage; llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::ExternalLinkage;
llvm::Constant* _init = 0; llvm::Constant* _init = 0;
@ -572,6 +575,7 @@ void VarDeclaration::toObjFile()
if (aliassym) if (aliassym)
{ {
Logger::println("alias sym");
toAlias()->toObjFile(); toAlias()->toObjFile();
return; return;
} }
@ -608,35 +612,13 @@ void VarDeclaration::toObjFile()
Logger::println("Creating global variable"); Logger::println("Creating global variable");
std::string _name(mangle()); std::string _name(mangle());
llvm::GlobalVariable* gvar = new llvm::GlobalVariable(_type,_isconst,_linkage,0,_name,M); bool emitRTstaticInit = false;
llvmValue = gvar;
if (!(storage_class & STCextern) && (getModule() == gIR->dmodule || istempl)) if (!(storage_class & STCextern) && (getModule() == gIR->dmodule || istempl))
{ {
if (parent && parent->isFuncDeclaration() && init && init->isExpInitializer()) { if (parent && parent->isFuncDeclaration() && init && init->isExpInitializer()) {
_init = DtoConstInitializer(t, NULL); _init = DtoConstInitializer(t, NULL);
// create a flag to make sure initialization only happens once emitRTstaticInit = true;
llvm::GlobalValue::LinkageTypes gflaglink = istempl ? llvm::GlobalValue::WeakLinkage : llvm::GlobalValue::InternalLinkage;
std::string gflagname(_name);
gflagname.append("__initflag");
llvm::GlobalVariable* gflag = new llvm::GlobalVariable(llvm::Type::Int1Ty,false,gflaglink,DtoConstBool(false),gflagname,M);
// check flag and do init if not already done
llvm::BasicBlock* oldend = gIR->scopeend();
llvm::BasicBlock* initbb = new llvm::BasicBlock("ifnotinit",gIR->topfunc(),oldend);
llvm::BasicBlock* endinitbb = new llvm::BasicBlock("ifnotinitend",gIR->topfunc(),oldend);
llvm::Value* cond = gIR->ir->CreateICmpEQ(gIR->ir->CreateLoad(gflag,"tmp"),DtoConstBool(false));
gIR->ir->CreateCondBr(cond, initbb, endinitbb);
gIR->scope() = IRScope(initbb,endinitbb);
elem* ie = DtoInitializer(init);
if (!ie->inPlace()) {
DValue* dst = new DVarValue(t, gvar, true);
DtoAssign(dst, ie);
delete dst;
}
gIR->ir->CreateStore(DtoConstBool(true), gflag);
gIR->ir->CreateBr(endinitbb);
gIR->scope() = IRScope(endinitbb,oldend);
} }
else { else {
_init = DtoConstInitializer(t, init); _init = DtoConstInitializer(t, init);
@ -668,11 +650,16 @@ void VarDeclaration::toObjFile()
//assert(0); //assert(0);
} }
} }
Logger::cout() << "final init = " << *_init << '\n';
gvar->setInitializer(_init);
} }
if (_init && _init->getType() != _type)
_type = _init->getType();
llvm::GlobalVariable* gvar = new llvm::GlobalVariable(_type,_isconst,_linkage,_init,_name,M);
llvmValue = gvar;
if (emitRTstaticInit)
DtoLazyStaticInit(istempl, gvar, init, t);
llvmDModule = gIR->dmodule; llvmDModule = gIR->dmodule;
//if (storage_class & STCprivate) //if (storage_class & STCprivate)

View file

@ -3,7 +3,28 @@ module phobos;
import import
std.array, std.array,
std.ctype, std.ctype,
std.intrinsic,
std.stdint,
std.stdio, std.stdio,
std.stdarg, std.stdarg,
std.uni, std.uni,
std.utf; std.utf,
std.c.fenv,
std.c.locale,
std.c.math,
std.c.process,
std.c.stdarg,
std.c.stddef,
std.c.stdio,
std.c.stdlib,
std.c.string,
std.c.time;
version(linux) {
import
std.c.linux.linux,
std.c.linux.linuxextern,
std.c.linux.pthread,
std.c.linux.socket;
}

View file

@ -12,7 +12,7 @@
module std.c.linux.linuxextern; module std.c.linux.linuxextern;
extern (C) extern extern (C)
{ {
void* __libc_stack_end; void* __libc_stack_end;
int __data_start; int __data_start;

View file

@ -331,7 +331,7 @@ union in6_addr
} }
const in6_addr IN6ADDR_ANY = { s6_addr8: [0] }; const in6_addr IN6ADDR_ANY;
const in6_addr IN6ADDR_LOOPBACK = { s6_addr8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] }; const in6_addr IN6ADDR_LOOPBACK = { s6_addr8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] };
//alias IN6ADDR_ANY IN6ADDR_ANY_INIT; //alias IN6ADDR_ANY IN6ADDR_ANY_INIT;
//alias IN6ADDR_LOOPBACK IN6ADDR_LOOPBACK_INIT; //alias IN6ADDR_LOOPBACK IN6ADDR_LOOPBACK_INIT;

26
test/bug51.d Normal file
View file

@ -0,0 +1,26 @@
module bug51;
import std.stdint;
union in6_addr
{
private union _in6_u_t
{
uint8_t[16] u6_addr8;
uint16_t[8] u6_addr16;
uint32_t[4] u6_addr32;
}
_in6_u_t in6_u;
uint8_t[16] s6_addr8;
uint16_t[8] s6_addr16;
uint32_t[4] s6_addr32;
}
const in6_addr IN6ADDR_ANY = { s6_addr8: [0] };
const in6_addr IN6ADDR_LOOPBACK = { s6_addr8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] };
void main()
{
}

48
test/union7.d Normal file
View file

@ -0,0 +1,48 @@
module union7;
pragma(LLVM_internal, "notypeinfo")
struct Union
{
union {
double g;
struct {
short s1,s2,s3,s4;
}
}
union {
float f;
long l;
}
}
Union a = { f:4f };
Union b = { 3.0, f:2 };
Union c = { l:42, g:2.0 };
Union d = { s2:3 };
Union e = { s1:3, s4:4, l:5 };
void main()
{
assert(a.f == 4f);
assert(a.g !<>= 0.0);
assert((a.l>>>32) == 0);
assert(b.g == 3.0);
assert(b.f == 2f);
assert(c.l == 42);
assert(c.g == 2.0);
assert(d.s1 == 0);
assert(d.s2 == 3);
assert(d.s3 == 0);
assert(d.s4 == 0);
{assert(d.f !<>= 0f);}
{}
assert(e.s1 == 3);
assert(e.s2 == 0);
assert(e.s3 == 0);
{assert(e.s4 == 4);}
{}
assert(e.l == 5);
}