Function calls and code size

Hi, my problem is rather a generic programming one, but might be Arduino-specific as well.
My code had a lot of inline pieces of working code. I began optimizing it for handling multiple input sources by moving these to a single event handler function which takes numeric event codes and their parameters (5 bytes and a single int) as its parameters.
But as I moved more and more code pieces and replaced them with calls to the handler, go figure, the code size increased by several percents, which is a big problem because the project is already tight in the flash. I had the impression that the compiler went mad and inlined the handler, so I tried to disable that behavior using the attribute((noinline)) directive, but that made no difference.
Why is this happening? Do function calls with several parameters have such a footprint in code size? Maybe am I better off with macros, just to keep the code nice and organized?
The entire code is rather huge and I doubt anyone would read it (and anyway, can´t access it right now from my workplace), but if it´s actually needed, I can paste the relevant parts later.
Thanks in advance!

We can only guess if you don’t share the actual code. are you passing by reference or by value? Callback functions are often a way to reduce RAM usage but the cost is usually an increase in program size, due to the calls.

Ok, here’s the (pretty incomplete and immature) handler code, complete with the functions called from it:

void startBFade(unsigned int steps, byte target) {
  bFadeFrom=bfBrightness;
  bFadeTo=target;
  bFadeTimer=steps;
  bFadeTimerStart=steps;
  bFade=true;
}


void power(bool turnOn) {
  if (turnOn) {
    if(effType==ET_STATIC) startBFade(BFADET_PWRON, 255);
    if(effType==ET_GRAD) {
      outColor=COL_BLACK;
      startBFade(BFADET_PWRON, 255);
      effectActive=true;
    }

    bcNewData=true;
    isOn = true;

  } else {
    effectActive=false;
    bFadeToBlack();
    isOn = false;
  }

}


int btnAdjAmount(byte repCnt) {
  if (repCnt < 6) {
    return 1;
  } else if (repCnt < 11) {
    return 5;
  } else {
    return 15;
  }

}


void adjustByte(byte* target, byte amount, bool add, byte lowerBound, byte upperBound){
  if(add) *target = (*target+amount < upperBound) ? *target+amount : upperBound;
  else *target = (*target-amount > lowerBound) ? *target-amount : lowerBound;
}

void command(byte ob, byte obParam, byte op, int opParam, byte repCount) {

   if(isOn) {
     switch(ob) {
      case CMD_OB_NONE: break;

      case CMD_OB_POWER:
        switch(op){
          case CMD_OP_OFF:
          case CMD_OP_TOGGLE:
            if (repCount==0) power(false);
            break;
        }
        break;

      case CMD_OB_BRIGHTNESS:
        switch(op) {
          case CMD_OP_SET:
            if((opParam>=0) && (opParam<=255)) setBrightness=opParam;
            break;
          case CMD_OP_INC:
            adjustByte(&setBrightness, opParam>0 ? opParam : btnAdjAmount(repCount), true, 0, 255);
            break;
          case CMD_OP_DEC:
            adjustByte(&setBrightness, opParam>0 ? opParam : btnAdjAmount(repCount), false, 0, 255);
            break;
        }
        break;

      case CMD_OB_CHANNEL:
        if((obParam < 0) || (obParam >= NUMCHAN)) break;
        switch(op) {
          case CMD_OP_SET:
            setColor.vals[obParam]=opParam;
            break;

          case CMD_OP_INC:
            adjustByte(&setColor.vals[obParam], opParam>0 ? opParam : btnAdjAmount(repCount), true, 0, 255);
            copyColor();
            break;

          case CMD_OP_DEC:
            adjustByte(&setColor.vals[obParam], opParam>0 ? opParam : btnAdjAmount(repCount), false, 0, 255);
            copyColor();
            break;
        }
        break;

      case CMD_OB_PARAM:
        if((obParam < 0) || (obParam >= NUMPARAMS)) break;
        if(currEff == NULL) break;
        switch(op) {
          case CMD_OP_SET:
            currEff->params[obParam]=opParam;
            break;

          case CMD_OP_INC:
            adjustByte(&currEff->params[obParam], opParam>0 ? opParam : btnAdjAmount(repCount), true, 0, currEff->paramsMax[obParam]);
            break;

          case CMD_OP_DEC:
            adjustByte(&currEff->params[obParam], opParam>0 ? opParam : btnAdjAmount(repCount), false, 0, currEff->paramsMax[obParam]);
            break;
        }


     }

   } else {
      switch(ob) {

        case CMD_OB_POWER:
          switch(op) {
            case CMD_OP_ON:
            case CMD_OP_TOGGLE:
              if (repCount==0) power(true);
              break;
          }

          break;

      }

   }

}

The entire code is rather huge and I doubt anyone would read it (and anyway, can´t access it right now from my workplace), but if it´s actually needed, I can paste the relevant parts later.

I suggest you post the entire sketch as an attachment. When there are memory usage issues, people here can often suggest huge memory footprint improvements, just by skimming the code without going into the details.

Memory usage isn't the issue, that's just fine, compiled code size is the problem after the aforementioned change. The code was mostly OK, but I wanted to make it easier to maintain and save some more flash space by creating a single event handler (or better said, task performer) for common tasks and calling it instead of having redundant code pieces (even if small ones) here and there, but it turned out to be contraproductive, and I just wonder why. It's more of a general programming issue than a problem with this particular project.

Do function calls with several parameters have such a footprint in code size?

Each time you do this, the compiler has to emit code to process all the parameters and push them onto the “stack” and then remove them from the stack at the end of the function code.

minimize the above by putting the parameters into a structure and using a pointer to the structure when you call the function. The function can then find it’s parameters using the pointer and the offset into the structure to get to each parameter.

Paul

szalipszki:
It's more of a general programming issue than a problem with this particular project.

I see, so it's a flash issue. Well people can often help with that too. I take this to mean that you don't want to post your code. Thing is, general programming issues are infinitely varied, and have fuzzy boundaries. So a general conversation about your programming techniques, without actually seeing them, creates a barrier in the first place. Then there is the vagueness and generality of answers and suggestions in reply, create another barrier. That is why it is always much more productive to look at concrete examples.

However, when the requester decides what parts are problematic or relevant, they is an idea that they already know where the problem lies. Time and time again on this forum, that assumption has been shown to be incorrect. The fact that the problem can't be found, is a good sign that the designer does not, in fact, have any idea where the problem is.

This is why you will often see an immediate request for code, such as reply #1 here.

Quote

Do function calls with several parameters have such a footprint in code size?

Each time you do this, the compiler has to emit code to process all the parameters and push them onto the "stack" and then remove them from the stack at the end of the function code.

That’s not quite how the usual arduino abis work. Normally, parameters will be passed in registers.