This case is trivial because a constant can be "sized down" to a byte easily, but when you are optimizing and assigning values to *registers*, promotions come into play much more.
As I said before, it's possible to fix this up to a point. I have already spent several month on this. In the next version, your example will look something like this:
...
; unsigned char a;
; a = 1;
B101 mov cl,1
; a = a + 3;
80C103 add cl,3
...
It's a matter of adding more operation "patterns". The problem is that it's getting hard when you start combining simple operations into longer sequences. A single binary operation, like add (char1 + char2) can be handled, but two or more such operations are at least *very* hard. Adding char1 + char2 + char3, is one addition with an intermediate result, then this is added with char3 etc.
Another problem with adding more "patterns" is that the pattern-matching code in LCC will quickly bloat. With the current changes, the compiler is some 40-50% bigger. The "size of a diskette" is not a factor anymore, but I don't want the compiler to get too big either...
Here are some more examples:
; a = b = c = 1;
C645FF01 mov byte ptr [ebp-1],1
B301 mov bl,1
B101 mov cl,1
; a = b + c;
88D9 mov cl,bl
024DFF add cl,byte ptr [ebp-1]
Here it starts to look ugly again...
; a = a + b + c;
0FB6C1 movzx eax,cl
0FB6D3 movzx edx,bl
01D0 add eax,edx
0FB655FF movzx edx,byte ptr [ebp-1]
01D0 add eax,edx
88C1 mov cl,al
Pelle