From 060417b0dc1018adc79fd16d0eb97c61d729aea3 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sat, 31 Oct 2020 21:27:41 +0100 Subject: [PATCH] Adjust type of int constants that fit in char When there is an integral constant like `3` in an expression, it has type `int` according to the C spec, even though it can be represented as an `unsigned char`. Change codegen (`hie_internal` and `typeadjust`) to treat it as `unsigned char` instead of `int` so that faster, unsigned operations can be used. For the test case in #1298, reduces the cycles per iteration from 4311 to 1884. This is a great improvement, but still much worse than the 1053 cycles/iter from `c4698df~`, so more work remains to be done. Further partial fix for #1298 and #1308. --- src/cc65/expr.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 19572944e..7ddd8f43a 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -219,6 +219,13 @@ static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush) /* Generate type adjustment code if needed */ ltype = TypeOf (lhst); + if (ED_IsConstAbsInt (lhs) && ltype == CF_INT && lhs->IVal >= 0 && lhs->IVal < 256) { + /* If the lhs is a int constant that fits in an unsigned char, use unsigned char. + ** g_typeadjust will either promote this to int or unsigned int as appropriate + ** based on the other operand. See comment in hie_internal. + */ + ltype = CF_CHAR | CF_UNSIGNED; + } if (ED_IsLocNone (lhs)) { ltype |= CF_CONST; } @@ -227,6 +234,9 @@ static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush) ltype |= CF_PRIMARY; } rtype = TypeOf (rhst); + if (ED_IsConstAbsInt (rhs) && rtype == CF_INT && rhs->IVal >= 0 && rhs->IVal < 256) { + rtype = CF_CHAR | CF_UNSIGNED; + } if (ED_IsLocNone (rhs)) { rtype |= CF_CONST; } @@ -2502,6 +2512,15 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ } } else if (lconst && (Gen->Flags & GEN_COMM) && !rconst) { + /* If the LHS constant is an int that fits into an unsigned char, change the + ** codegen type to unsigned char. If the RHS is also an unsigned char, then + ** g_typeadjust will return unsigned int (instead of int, which would be + ** returned without this modification). This allows more efficient operations, + ** but does not affect correctness for the same reasons explained in g_typeadjust. + */ + if (ltype == CF_INT && Expr->IVal >= 0 && Expr->IVal < 256) { + ltype = CF_CHAR | CF_UNSIGNED; + } /* The left side is constant, the right side is not, and the ** operator allows swapping the operands. We haven't pushed the @@ -2536,6 +2555,10 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ unsigned rtype = TypeOf (Expr2.Type); type = 0; if (rconst) { + /* As above, but for the RHS. */ + if (rtype == CF_INT && Expr2.IVal >= 0 && Expr2.IVal < 256) { + rtype = CF_CHAR | CF_UNSIGNED; + } /* Second value is constant - check for div */ type |= CF_CONST; rtype |= CF_CONST;