/* Copyright Per Bothner 1987. Read the file Q-INFO */
/* Extract 
	movl sp@(4),a0	| 206F 0004	4 bytes
	movl a0@(n),d0	| 2028 n	4 bytes
	rts		| 4e75		2 bytes
*/
#include <gfunc.h>
#include <parsefile.h>
#include <Qcompile.h>
#include <procs.h>
#include <exceptions.h>
//#include <hash.h>
#include <builtin-syms.h>
#include <newtype.h>
/*#include <methods.h>*/
#include "traverse.h"
#include <stdlib.h>
extern struct Any SimplifyAny(struct Any);
extern long AnyToLong(struct Any);

#if 0
struct Extract
  {
    long _move_parm;
    short _index;
    short offset;
    short _return;
  };

#define __MakeExtract(p, n) \
    p->_move_parm = 0x206F0004;	\
    p->_index = 0x2028;		\
    p->offset = n;		\
    p->_return = 0x4e75;

#define MinExtractFix (-8)
#define MaxExtractFix 8

static struct Extract *ExtractTab[MaxExtractFix - MinExtractFix + 1] = {0};

struct Extract *
CreateExtract(int offset)
  { register struct Extract *p = AllocStd(Extract);
    __MakeExtract(p, offset);
    return p;
  }

struct Extract *
MakeExtract(int object_offset)
  { int index = object_offset - MinExtractFix;
    register struct Extract **p = &ExtractTab[index];
    if (index < 0 || object_offset > MaxExtractFix)
	return CreateExtract(object_offset*sizeof(Object));
    if (*p == 0)
	*p = CreateExtract(object_offset*sizeof(Object));
    return *p;
  }

PrintExtract(struct Extract *p, f)
  {
    fprintf(f, "DEREF %g", p->offset / 4.0);
  }

Object
DoExtract(Object *object, int object_offset)
  {
    if (object_offset > 0x20000) object_offset = *(long*)object_offset;
    return object[object_offset];
  }
#endif

#if 0
struct ExtractExpr *
AllocExtract(Expr *source, Expr *offset, int stepSize)
{
    register struct ExtractExpr *ex = new ExtractExpr();
    ex->source.E = source; ex->offset.E = offset; ex->stepSize = stepSize;
    if (stepSize == -1) { /* lvalue */
	ex->type = MakeExType(ExPointerType, FldPtrT);
    }
    else if (stepSize == -2) {/* rvalue */
	if (ex->offset.decl->is_const())
	    return (struct ExtractExpr*)new ExprQuote(ex->offset.decl->type);
	else if (!ex->offset.decl->is_proc()
	    ex->type = ex->offset.decl->type;
    }
    return ex;
}
#endif

#if 0
struct Type *
GetBitFieldType(Name name, int size, int align)
{
    extern struct Type *AllocType();
    extern struct ClassDesc _BitFieldDummy_[1];
    struct Type *type = AllocType(0);
    type->name = name;
    type->desc = _BitFieldDummy_;
    type->size = (size + 7) >> 3;
    type->excess_bits = (type->size << 3) - size;
    BITS_TYPE_ALIGN(type) = align;
    type->options = 0;
    if (name == sSigned) type->options |= BITS_TYPE_SIGNED_OPTION;
    BITS_TYPE_TYPE(type) = IntT;
    BITS_TYPE_OFFSET(type) = 0;
    type->kind = BitsTypeKind;
    return type;
}
#endif

#if 0
extern Expr *ParseLook();
extern Name ParseIdent();
Expr *
ParseBitField(struct ParseFile *ff, int kind)
  { int ch = ff->get();
    int size;
    int align; /* log2 of alignment wanted (in bits) */
    Name macro = ff->macro;
    struct Type *type;
 fprintf(stderr, "NEED TO FIX BIT-FIELD CODE TO USE NEW BitsTypeKind!!!\n");
    if (kind == 3) /* bytes */ align = 3;
    else align = 0;
    if (ch == '%')
      {
	Expr_Ptr exp; Name ss;
	exp.P= NULL;
	ss = ParseIdent(ff);
#if 0
	if (kind == 3) ;
	else if (ss == EnterSymbol("long"))
	  { exp.P = AllocFixInt(32); align = 4; }
	else if (ss == EnterSymbol("short"))
	  { exp.P = AllocFixInt(16); align = 4; }
	else if (ss == EnterSymbol("char"))
	  { exp.P = AllocFixInt(8); align = 3; }
#endif
	if (exp.E == NULL)
	  {
	    if ((Expr*)ss != FailedParse) ParseUnget(ss, ff);
	    exp.E = ParseLook(ff, ONEprio);
	  }
	exp.E = TestConstant(exp.E);
	if (exp.E == NULL
#if 0
        || !ExprIsType(exp, FixInt)
#endif
	) {
	    ParseError(ff, "F non-constant bitfield size");
	    return FailedParse;
	  }
	size = *(long*)exp.P;
	if (kind == 3) size *= 8;
      }
    else
      {
	if (ch != EOF) ff->putback(ch);
	size = kind == 3 /*bytes*/ ? 1 : 32;
      }
    return new ExprQuote(GetBitFieldType(macro, size, align));
  }

Expr * ExtractExpr::traverse(struct TraverseData *data)
  {
    source = source.traverse(data);
    if (stepSize >= 0)
	offset = offset.traverse(data);
    return (Expr*)this;
  }
#endif

#if 0
SetBitField(RootPtr src, unsigned *addr, int offset, int size)
  { unsigned long mask;
    long i = AnyToLong(SimplifyAny(MAKE_ANY(src, NULL)));
    addr = (unsigned*)((short*)addr + (offset >> 4));
    offset &= 15;
    if (size + offset > 32) RaiseDomainError(NoValue);
    mask = ((unsigned)(-1) >> (32-size)) << (32-size-offset);
    *addr &= ~mask;
    *addr |= (i << (32-size-offset)) & mask;
  }

CopyStruct(Object src, Object dst, struct Type *type)
  { register struct RecordField *field;
    int offset = 0;
    register char *sTop = (char*)src, *dTop = (char*)dst;
#if 0
    struct Field *field = (struct Field*)type->fieldList;
    for (;;) {
	int delta = field->next;
	if (delta == 0) break;
	field += delta;
	... as below ...
    }
#endif
    struct ClassDesc *desc = type->desc;
    struct Field *fld;
    FOR_EACH_FIELD(fld, desc) {
	if (fld->kind != Pointer_Field && fld->kind != Include_Field)
	    continue;
	if (fld->offset == -1) continue;
	bcopy(sTop+offset, dTop+offset, fld->offset - offset);
	offset = fld->offset;
	if (fld->kind == Pointer_Field) {
	    /* NOTE: should do AddLink */
	    *(Object*)(sTop+offset) = *(Object*)(dTop+offset);
	    offset += sizeof(Object);
	}
	else /* DeclIsInclude(field) */ {
	    CopyStruct(sTop+offset, dTop+offset, ExType_Type(field->type));
	    offset += fld->u.type->size();
	}
      }
    bcopy(sTop+offset, dTop+offset, type->size() - offset);
    return;
}
#endif

void SetField(RootPtr src, void *dst, struct Field *fld)
{
    void *dst_addr = (char*)dst + fld->u.offset;
#if 0
    // Handled by ReferenceType::coerceFromRoot.
    if (src == NULL && fld->type->kind == ReferenceTypeKind)
	*(void**)dst_addr = src;
    else
#endif
	fld->type->coerceFromRoot(dst_addr, src);
}

#if 0
ExtractBitField(char *addr, struct Declaration *decl)
  { int offset = BitOffset(decl); int size = BitSize(decl);
    addr += offset >> 3;
    offset = (offset & 7) + (addr & 3) * 3;
    addr &= ~3;
abort();
  }

dst[for count] := src[for count] >> shift

if signed, and negative
    asm(" moveq #-1,d0");
else
    asm(" moveq #0,d0");
asm("shift_loop:");
asm(" swap d0");
asm(" movw src@+,d0");
asm(" movl d0,d1");
asm(" lsrl d1,shift");
asm(" movw d1,dst@+");
asm(" dbf count,shift_loop");
#endif

#if 0
CompileExtract(ex, cf, dest)
    struct ExtractExpr *ex; CFile *cf; struct DataToken *dest;
  {
#if 1
    struct DataToken tmp_token[1], *tmp_dest;
    extern struct DataToken *MakeNullToken();
    extern Object TestConstant();
    Object offset;
    if (ex->stepSize == -1 && ex->offset.decl->is_const())
	tmp_dest = IgnoreResultToken;
    else tmp_dest = MakeNullToken(tmp_token);
    CompileExpr(ex->source.E, cf, tmp_dest);
    if (ex->stepSize < 0)
      { struct Declaration *decl = (struct Declaration*)ex->offset.P;
	if (decl->is_const())
	    CompileLoadValue(decl->type, cf, dest);
	else if (ex->stepSize == -2 && decl->kind == Pointer_Field)
	    MoveIndexed(cf, tmp_token, decl->offset, dest);
	else {
	    CompileLoadValue(decl, cf, PushStackToken);
	    CompileExpr(ex->source.E, cf, PushStackToken);
	    CallExternC(cf,
		ex->stepSize == -2 ? "ExtractField" : "ApplyRecordField",
		0, 2, dest);
	}
	return;
      }
    offset = ex->offset.P;
    if (!IsFixInt(offset)) offset = TestConstant(offset);
    if (offset != NULL && IsFixInt(offset))
	MoveIndexed(cf, tmp_token, *(long*)offset * ex->stepSize, dest);
#else
    struct DataToken tmp_token;
    int tmp_reg = GetTempReg(cf, GetAddrReg);
    if (tmp_reg == -1) return; /* GetTempReg prints out error message */
    MakeRegToken(&tmp_token, tmp_reg);
    CompileExpr(ex->source, cf, &tmp_token);
    if (IsFixInt(ex->offset))
      {
	MakeRegOffsetToken(&tmp_token, tmp_reg, ex->offset->i*sizeof(Object));
	CompileMove(cf, &tmp_token, dest);
      }
#endif
    else {
	CompileLoadInt(cf, ex->stepSize);
	CompileExpr(ex->offset.E, cf, PushStackToken);
	CompileExpr(ex->source.E, cf, PushStackToken);
	CallExternC(cf, "DoExtract", 0, 3, dest);
    }
  }
#endif

#if 0
DoExtract(ob, offset, size)
    Object ob; long *offset, size;
{
    if (!HasHType(offset, FixInt)) RaiseDomainError(NoValue);
    size *= *offset;
    return *(long*)((char*)ob + size);
}
#endif

#if 0
Object
ExtractOldField(arg, rField)
    Object arg; struct Declaration *rField;
  {
    if (rField->is_const())
	return (Object)rField->type.ptr;
    if (rField->is_proc())
	return _Apply(arg, rField->type);
    if (IsBitField(rField)) {
	int offset = rField->offset;
	int size = BitSize(rField);
	arg = (Object)((short*)arg + (offset>>4));
	offset &= 15;
	if (size + offset > 32) { }
#if 0
	else if (signed) {
	    long i = *(long*)arg;
	    i <<= offset;
	    i >>= 32-size;
	    return AllocFixInt(i);
	}
#endif
	else {
	    unsigned long i = *(unsigned long*)arg;
	    i >>= 32-offset-size;
	    i &= (1<<size)-1;
	    return AllocFixInt(i);
	}
    }
    if (rField->kind == Pointer_Field)
	return *(Object*)((char*)arg + rField->offset);
    if (rField->type.ptr != NULL)
      { Object x = StdAllocOb(rField->type.ptr);
	CopyStruct((char*)arg + rField->offset, x, rField->type.ptr);
	return x;
      }
    Error(0, "ExtractField: cannot get byte fields");
  }
#endif

#if 0
Object
ExtractField(arg, fld)
    Object arg; struct Field *fld;
{
    if (!NEW_FIELD(fld)) return ExtractOldField(arg, fld);
    switch (fld->kind) {
      case Constant_Field: return (Object)fld->u.value;
      case Method_Field: return _Apply(arg, fld->u.value);
      case Pointer_Field:
	return *(Object*)((char*)arg + fld->offset);
      case Bit_Field: {
	int offset = fld->offset;
	int size = fld->u.b.size;
	arg = (Object)((short*)arg + (offset>>4));
	offset &= 15;
	if (size + offset > 32) { abort(); }
	else if (fld->u.b.is_signed) {
	    long i = *(long*)arg;
	    i <<= offset;
	    i >>= 32-size;
	    return AllocFixInt(i);
	}
	else {
	    unsigned long i = *(unsigned long*)arg;
	    i >>= 32-offset-size;
	    i &= (1<<size)-1;
	    return AllocFixInt(i);
	}
     }
      default:
    Error(0, "ExtractField: not implemented");
    }
}
#endif

#if 0 /* BUG in gcc 1.18 */
#undef MAKE_ANY
struct Any
MAKE_ANY(void *addr, struct Type *type) { return (struct Any){type, addr};}
#endif

struct Any ExtractBitType(void *arg, struct Type *type)
{
    int offset = BITS_TYPE_OFFSET(type);
    int size = BITS_TYPE_BLENGTH(type);
    long i;
    void *addr = (short*)arg + (offset>>4);
    offset &= 15;
    if (size + offset > 32) { abort(); }
    else if (BITS_TYPE_SIGNED(type)) {
	i = *(long*)addr;
#ifdef BITS_BIG_ENDIAN
	i <<= offset;
#else
	i <<= 32-offset-size;
#endif
	i >>= 32-size;
    }
    else {
	unsigned long ui = *(unsigned long*)addr;
#ifdef BITS_BIG_ENDIAN
	ui >>= 32-offset-size;
#else
	ui >>= offset;
#endif
	ui &= (1<<size)-1;
	i = (long)ui;
    }
#if 0
    if (BITS_TYPE_MAPPED(type))
	return MAKE_ANY(BITS_TYPE_MAPPING(type)[i], BITS_TYPE_TYPE(type));
    else
#endif
	return MAKE_ANY((void *)i, BITS_TYPE_TYPE(type));
}

struct Any Field::extract(void *base) const
{
    void *addr;
    switch (indirection) {
      case IsConst:
	addr = u.value;
	break;
      case IsField:
	addr = (void*)((char*)base + u.offset);
	break;
      case IsIndirect:
	addr = (void*)(*(char**)((char*)base + offset1()) + offset2());
	break;
    }
    switch (kind) {
      case Method_Field:
#if 0
/*	if (fld->u.func->flags & FunctionNeedsEnvType)*/
	  {
	    struct Partial *partial = Alloc(PartialT, sizeof(struct Partial));
	    partial->fc.func = fld->u.value;
	    partial->fc.unnamedParams = 0;
	    partial->unnamedParams = 0;
	    partial->unnamedParams = 0;
	    partial->env = arg;
	    partial->envtype = NULL;
/* abort();*/
	    return MAKE_ANY(partial, NULL);
/*	    return MAKE_ANY(partial, PartialT);*/
	}
/*	else return MAKE_ANY(arg, faddr());*/
#else
	return MAKE_ANY(new GFunction(faddr(), (Root*)base), (Type*)Root::desc());
#endif
#if 0
      case Bit_Field: {
	int offset = fld->offset;
	int size = fld->u.b.size;
	void *addr = (short*)arg + (offset>>4);
	offset &= 15;
	if (size + offset > 32) { abort(); }
	else if (fld->u.b.is_signed) {
	    long i = *(long*)addr;
	    i <<= offset;
	    i >>= 32-size;
	    return MAKE_ANY((void *)i, FixNumT);
	}
	else {
	    unsigned long i = *(unsigned long*)addr;
	    i >>= 32-offset-size;
	    i &= (1<<size)-1;
	    return MAKE_ANY((void *)i, FixNumT);
	}
    }
#endif
      default:
	  return MAKE_ANY(addr, type);
    }
}

Root * Field::coerceToRoot(void *base) const
{
    switch (kind) {
      case Constant_Field: return (Root*)faddr();
      case Method_Field:
	return new GFunction((Function*)faddr(), (Root*)base);
      case Pointer_Field:
	return *(Root**)((char*)base + u.offset);
      default: abort();
    }
}

struct FldPtr { Object addr; struct Declaration *decl; };

#if 0
Object
FldPtrExtract(struct FldPtr *fld)
{
    return ExtractField(fld->addr, fld->decl);
}

extern "C" {
void FldPtrSet(Object new_ob, struct FldPtr *fld)
{ SetField(new_ob, fld->addr, (struct Field*)fld->decl); }

} /* end extern "C" */

Object
ApplyRecordField(arg, decl)
    Object arg; struct Declaration *decl;
{   register struct FldPtr *fld = AllocStd(FldPtr);
    fld->addr = arg;
    fld->decl = decl;
    return (Object)fld;
}
#endif

void DumpRecordField(RecordField *rField, CFile *cf, Label *label)
  {
#if 1
abort();
#else
    int dot;
    if (EvalCompile(cf))
      { struct DataToken val_token[1];
	MakeLabelToken(val_token, NULL, rField, 0);
	CompLabelEqual(cf, label, val_token);
	return;
      }
    dot = CompStdPrefix(cf, label, &RecordField);
    if (rField->next == NULL)
	Comp1Long(cf, 0, "'next=END");
    else CompileLabelTo(cf, rField->next);
    if (rField->type.ptr == NULL)
	Comp1Long(cf, 0, "'type=DIRECT");
    else if (rField->is_const() || rField->is_proc()) {
	struct DataToken token;
	Object x = (Object)rField->type.ptr;
	if (IsVariable(x) && IsBound((Var*)x))
	    x = ((Var*)x)->v.varlink.o;
	if (rField->flags & LookupDeclaration)
	    MakeLabelToken(&token, SymbolString(rField->name), 0, 0);
	else
	    MakeGenLabel(&token, cf->generate_label_number());
	MakeTokenTo(cf, &token, x);
	CompTokenPtr(cf, &token, 0);
    }
    else CompPtr(cf, rField->type->name, 0, "'type");
    Comp1Short(cf, rField->offset, "'offset");
    Comp1Short(cf, rField->size, "'size");
    CompileLabelTo(cf, rField->name);
    Comp2Byte(cf, rField->flags, rField->blockLevel);
    return dot;
#endif
  }

#if 0
OptimizeToExtract(cf, expr_arg, arg_type, name, dest)
/*should be done at Optimze time, not Compile time */
    CFile *cf;
    struct Expression *expr_arg;
    struct Type *arg_type;
    Name name;
    struct DataToken *dest;
  {
    struct DataToken tmp_token[1];
    int offset;
    struct RecordField *rField;
    if (arg_type == NULL) return 0;
    if (HasHType(arg_type, Block))
      {
	for (rField = ((struct Block*)arg_type)->decls.first;
	    rField != NULL && rField->name != name; rField = rField->next) { }
      }
    else
	rField = (struct RecordField*)Search(arg_type->hash, name);
    if (rField == NULL || !HasHType(rField, RecordField)) return 0;
    if (rField->size != 0)
	CompileError(cf, "%x's is a DIRECT (non-object) type", expr_arg, name);
    offset = rField->offset;
    MakeNullToken(tmp_token);
    CompileExpr(expr_arg, cf, tmp_token);
    MoveIndexed(cf, tmp_token, offset, dest);
    return 1;
  }
#endif

#if 0
CompCopyStruct(src, dst, type, cf)
    struct DataToken *src, *dst;
    struct Type *type;
    CFile *cf;
/* compile: " struct 'type' *'src', *'dst'; *'dst' = *'src'; " */
/* NOTE: ought to be implemented with auto-increment */
  { struct RecordField *field;
    struct DataToken s[1], d[1];
    for (field = type->fieldList; field != NULL; field = field->next)
      {
	if (field->size == 0)
	  {
	    MakeGenOffsetToken(s, src, field->offset);
	    MakeGenOffsetToken(d, dst, field->offset);
/* NOTE: should increment (run-time) ref. count of s */
	    CompileCopy(s, d, cf);
	    FreeToken(s, cf); FreeToken(d, cf);
	  }
	else if (field->type != NULL)
	  {
	    MakeGenOffsetToken(s, src, field->offset);
	    MakeGenOffsetToken(d, dst, field->offset);
	    MakeLiteralToken(s); MakeLiteralToken(d);
	    CompCopyStruct(s, d, field->type, cf);
	    FreeToken(s, cf); FreeToken(d, cf);
	  }
	else
	  { /* copy bytes */
	    int more = field->size, offset = field->offset;
	    while (more > 0)
	      {
		if ((offset & 1) || more == 1)
		  {
		    CompCopyByte();
		    offset++; more--;
		  }
		else if (more < 4)
		  {
		    CompCopyShort();
		    offset += 2; more -= 2;
		  }
		else
		  {
		    CompCopyLong();
		    offset += 4; more -= 4;
		  }
	      }
	  }
      }
  }
#endif 0

#if 0
Object
IBitOr(a, b) long *a, *b;
  { return AllocFixInt(*a | *b); }
#endif


#if 0
extern struct ClassDesc _BitFieldDummy_[1];
struct Type CharFieldT[1] = {{
    "char", 1, NULL, (Func)&CharTable, _BitFieldDummy_, NULL /*SymbolT*/, 0,
    BitsTypeKind, 0, 3, 0, BITS_TYPE_MAPPED_OPTION
}};

void BitFieldUnify(struct Any self, struct Any other)
{
    Unify(ExtractBitType(self.addr, self.type), other);
}

int BitFieldCompare(struct Any self, struct Any other)
{
    if (other.type->kind == BitsTypeKind)
	other = ExtractBitType(other.addr, other.type);
    Compare_(ExtractBitType(self.addr, self.type), other);
}

void BitFieldPrint(struct Any self, FILE *file)
{
    TypedPrint(ExtractBitType(self.addr, self.type), file);
}

int BitFieldHash(struct Any self)
{
    self = ExtractBitType(self.addr, self.type);
    return (*(IntMethod)self.type->desc->stdMethods[StdM_hashValue])(self);
}
#endif
