Strategy to implement 64-bit contants for lcc

  • Follow


I am in the process of retargetting lcc 4.2 for a 32-bit architecture.
However I want it to support 64bit arithmetic. Implementing support
for the 64bit terminal patterns  (e.g. ADDI8, ASGNI8, etc) in the
backend was not that difficult. I was able to generate code that
supports 64bit arithmetic.

When I was about to create test cases for the 64bit arithmetic I
wanted to use long long constants (e.g. 12345678987654321LL). In order
to do that I modified the value union in c.h and added:

	long long ill;
	unsigned long long ull;

members to be able to store 64bit contants. I was succesful in reading
contants by appropriately modify the icon() function in lex.c and
using their value in my backend md file. Tweeking files sym.c, types.c
and stab.c where needed to utilize the new 64 contants was not
difficult either.

The problem is the simplification functions (e.g. simplify) in simp.c
that perform contant folding, casting etc. The amount of changes seem
to be overwhelming.

In all my changes I am handing 64bit contants conditionally by the
size of the type. In lcc 4.2 all integer contants are of type INT or
UNSIGNED. There is a discepancy in the way integer types are defined
in the lcc book by Fraser & Hanson and the way that they are actually
implemented in lcc 4.2. According to the book the basic integer types
have distinct operands (type->op) according to their sizes (e.g. CHAR,
SHORT, INT, UNSIGNED) while in the lcc4.2 implementation (types.c)
they are either INT or UNSIGNED. I was debating of whether I should
introduce a LONGLONG or a LONG+LONG type and handle the long long
constants using case statements on the type->op instead of conditional
statements on the type->size.

Given that lcc-win32 supports long long constants I am sure these
concerns have been addressed before. I would like to get some thoughs
and feedback on how I should go about implementing long long
constants. If there is source code available out there I would be glad
to obtain it.
0
Reply kostas 1/4/2008 7:20:03 PM

As you have seen, the problem is the simplify module, where
you will have to add all the conversions, constant folding and whatever.

For instance

int main(void)
{
         long long m = 4776LL + 5776.87L + 6U;
         printf("%lld\n",m);
}

This will print 10558, without any run time operation.
Obviously each operation will have to be added to simplify. That
function source code is around 1900 lines already...

I do not understand how you implement long long without creating a new
type  long long...

Another core will be to implement the WRITING of the constant in the
assembler. You will have to add several cases to defconst.

-- 
jacob navia
jacob at jacob point remcomp point fr
logiciels/informatique
http://www.cs.virginia.edu/~lcc-win32
0
Reply jacob 1/4/2008 9:27:13 PM


Thank you for your quick response.

>
> This will print 10558, without any run time operation.
> Obviously each operation will have to be added to simplify. That
> function source code is around 1900 lines already...
>

That is why I am dreading to adapt the code to handle 64-bit
constants.

> I do not understand how you implement long long without creating a new
> type =A0long long...
>

I do have a long long type just like the one in lcc 4.2. My question
in the following:
In lcc 3.6 each of the basic types like char, short, int unsigned int,
long etc are defined in types.c with each one having a distict
operator code that gives information about the size of the type. These
operator codes are CHAR, SHORT, INT, UNSIGNED etc.
In lcc 4.2 the basic types share only two operator codes namely INT
and UNSIGNED. This modification reflects in many places in the code
including the value union in c.h.

lcc 3.6 :
typedef union value {
	/* signed */ char sc;
	short ss;
	int i;
	unsigned char uc;
	unsigned short us;
	unsigned int u;
	float f;
	double d;
	void *p;
} Value;

lcc 4.2:

typedef union value {
	long i;
	unsigned long u;
	long double d;
	void *p;
	void (*g)(void);
} Value;

As a result the long value holds constant for all integer types up to
4 bytes. The double value holds values for both float and double
types. I have added

	long long ill;
	unsigned long long ull;

to the value union to be able to store 64 bit integer values. So far I
have been using conditional statements based on the type size in order
to access the long i or the long long ill when accessing the value of
a contant. For example in types.c I define the bounds of the long long
type as

	case INT:
	if (ty->size <=3D sizeof (long)) {
		p->u.limits.max.i =3D ones(8*ty->size)>>1;
		p->u.limits.min.i =3D -p->u.limits.max.i - 1;
	} else {
		p->u.limits.max.ill =3D (~0ULL)>>1;
		p->u.limits.min.ill =3D -p->u.limits.max.ill - 1;
	}
	break;

I am debating of whether I should create a new integer operator
LONGLONG or LONG+LONG in order to make that distinction. In that case
in the above example I would have:

	case INT:
		p->u.limits.max.i =3D ones(8*ty->size)>>1;
		p->u.limits.min.i =3D -p->u.limits.max.i - 1;
		break;
	case LONGLONG:
		p->u.limits.max.ill =3D (~0ULL)>>1;
		p->u.limits.min.ill =3D -p->u.limits.max.ill - 1;
		break;

Either of the two ways would work but I am not sure which one is the
most appropriate. The new version 4.2 is using conditional statements
to distinguish the float from the double type. The older version 3.6
is using switch statements on the type operator. Which of the two ways
do you recommend? How have you had that implemented in lcc-win32.?
Thank you again.

kostas.


0
Reply kostas 1/4/2008 11:26:47 PM

kostas@cs.utsa.edu wrote:
> Thank you for your quick response.
> 
>> This will print 10558, without any run time operation.
>> Obviously each operation will have to be added to simplify. That
>> function source code is around 1900 lines already...
>>
> 
> That is why I am dreading to adapt the code to handle 64-bit
> constants.
> 
>> I do not understand how you implement long long without creating a new
>> type  long long...
>>
> 
> I do have a long long type just like the one in lcc 4.2. My question
> in the following:
> In lcc 3.6 each of the basic types like char, short, int unsigned int,
> long etc are defined in types.c with each one having a distict
> operator code that gives information about the size of the type. These
> operator codes are CHAR, SHORT, INT, UNSIGNED etc.
> In lcc 4.2 the basic types share only two operator codes namely INT
> and UNSIGNED. This modification reflects in many places in the code
> including the value union in c.h.
> 
> lcc 3.6 :
> typedef union value {
> 	/* signed */ char sc;
> 	short ss;
> 	int i;
> 	unsigned char uc;
> 	unsigned short us;
> 	unsigned int u;
> 	float f;
> 	double d;
> 	void *p;
> } Value;
> 
> lcc 4.2:
> 
> typedef union value {
> 	long i;
> 	unsigned long u;
> 	long double d;
> 	void *p;
> 	void (*g)(void);
> } Value;
> 
> As a result the long value holds constant for all integer types up to
> 4 bytes. The double value holds values for both float and double
> types. I have added
> 
> 	long long ill;
> 	unsigned long long ull;
> 
> to the value union to be able to store 64 bit integer values. So far I
> have been using conditional statements based on the type size in order
> to access the long i or the long long ill when accessing the value of
> a contant. For example in types.c I define the bounds of the long long
> type as
> 
> 	case INT:
> 	if (ty->size <= sizeof (long)) {
> 		p->u.limits.max.i = ones(8*ty->size)>>1;
> 		p->u.limits.min.i = -p->u.limits.max.i - 1;
> 	} else {
> 		p->u.limits.max.ill = (~0ULL)>>1;
> 		p->u.limits.min.ill = -p->u.limits.max.ill - 1;
> 	}
> 	break;
> 
> I am debating of whether I should create a new integer operator
> LONGLONG or LONG+LONG in order to make that distinction. In that case
> in the above example I would have:
> 
> 	case INT:
> 		p->u.limits.max.i = ones(8*ty->size)>>1;
> 		p->u.limits.min.i = -p->u.limits.max.i - 1;
> 		break;
> 	case LONGLONG:
> 		p->u.limits.max.ill = (~0ULL)>>1;
> 		p->u.limits.min.ill = -p->u.limits.max.ill - 1;
> 		break;
> 
> Either of the two ways would work but I am not sure which one is the
> most appropriate. The new version 4.2 is using conditional statements
> to distinguish the float from the double type. The older version 3.6
> is using switch statements on the type operator. Which of the two ways
> do you recommend? How have you had that implemented in lcc-win32.?
> Thank you again.
> 
> kostas.
> 
> 


lcc-win32 forked from lcc at version 3.5, 12 years ago. Since then,
I have worked in THAT code base, and the new interface wasn't used
(lcc 4.1) since that would have meant an enormous work for me.

I have added the 64 bit type, the complex type, the boolean type,
the long double type, operator overloading etc.

12 years of work.

That is why simp.c is so big. I added many simplifications that
weren't there, and added constant folding, that is done in THAT module
also.



-- 
jacob navia
jacob at jacob point remcomp point fr
logiciels/informatique
http://www.cs.virginia.edu/~lcc-win32
0
Reply jacob 1/6/2008 12:09:07 PM

3 Replies
87 Views

(page loaded in 0.121 seconds)

5/22/2013 1:21:52 PM


Reply: