Main Content

Lower Estimate of Size of Local Variables

Total size of local variables in function taking nested scopes into account

Description

This metric provides an optimistic estimate of the total size of local variables in a function. The metric is the sum of the following sizes in bytes:

  • Size of function return value

  • Sizes of function parameters

  • Sizes of local variables

    Suppose that the function has variable definitions in nested scopes as follows:

    type func (type param_1, ...) {
    
      {
        /* Scope 1 */
        type var_1, ...;
      }
      {
        /* Scope 2 */
        type var_2, ...;
      }
    }
    The software computes the total variable size in each scope and uses whichever total is greatest. For instance, if a conditional statement has variable definitions, the software computes the total variable size in each branch, and then uses whichever total is greatest. If a nested scope itself has further nested scopes, the same process is repeated for the inner scopes.

    A variable defined in a nested scope is not visible outside the scope. Therefore, some compilers reuse stack space for variables defined in separate scopes. This metric provides a more accurate estimate of stack usage for such compilers. Otherwise, use the metric Higher Estimate of Size of Local Variables. This metric adds the size of all local variables, whether or not they are defined in nested scopes.

  • Additional padding introduced for memory alignment

Your actual stack usage due to local variables can be different from the metric value.

  • Some of the variables are stored in registers instead of on the stack.

  • Your compiler performs variable liveness analysis to enable certain memory optimizations. When computing this metric, Polyspace® does not consider these optimizations.

  • Your compiler uses additional memory during a function call. For instance, compilers store the address to which the execution returns following the function call. When computing this metric, Polyspace does not consider this hidden memory usage.

  • Compilers optimize temporary variables in different ways. This metric excludes temporary variables. Only the variables that are explicitly declared by the user are considered.

However, the metric provides a reasonable estimate of the stack usage due to local variables.

When a function returns a class, struct, or union that has a copy constructor, the compiler might perform a return value optimization. Instead of returning a class object, the function might use a pointer to pass the value. This optimization might change the value of this metric.

To determine the sizes of basic types, the software uses your specifications for Target processor type (-target). The metric also takes into account #pragma pack directives in your code.

Examples

expand all

int flag();

int func(int param) {
  int var_1;
  int var_2;
  if (flag()) {
      int var_3;
      int var_4;
    } else {
      int var_5;
    }
}

In this example, assuming four bytes for int, the lower estimate of local variable size is 24. The breakup of the metric is shown in this table.

VariableSize (in Bytes) Running Total
Return value4 4
Parameter param48
Local variables var_1 and var_24+4=816
Local variables defined in the if condition

max(4+4,4)= 8

The size of variables in the first branch is eight bytes. The size in the second branch is four bytes. The maximum of the two branches is eight bytes.

24

No padding is introduced for memory alignment because all the variables involved have the same type.

char func(char param) {
  int var_1;
  char var_2;
  double var_3;
}

In this example, assuming one byte for char, four bytes for int, eight bytes for double and four bytes for alignment, the lower estimate of local variable size is 20. The alignment is usually the word size on your platform. In your Polyspace project, you specify the alignment through your target processor. For more information, see the Alignment column in Target processor type (-target).

The breakup of the size is shown in this table.

VariableSize (in Bytes) Running Total
Return value1 1
Additional padding introduced before param is stored

0

No memory alignment is required because the next variable param has the same size.

1
Parameter param12
Additional padding introduced before var_1 is stored

2

Memory must be aligned using padding because the next variable var_1 requires four bytes. The storage must start from a memory address at a multiple of four.

4
var_148
Additional padding introduced before var_2 is stored

0

No memory alignment is required because the next variable var_2 has smaller size.

8
var_219
Additional padding introduced before var_3 is stored

3

Memory must be aligned using padding because the next variable var_3 requires eight bytes. The storage must start from a memory address at a multiple of the alignment, four bytes.

12
var_3820

The rules for the amount of padding are:

  • If the next variable stored has the same or smaller size, no padding is required.

  • If the next variable has a greater size:

    • If the variable size is the same as or less than the alignment on the platform, the amount of padding must be sufficient so that the storage address is a multiple of its size.

    • If the variable size is greater than the alignment on the platform, the amount of padding must be sufficient so that the storage address is a multiple of the alignment.

class MySimpleClass {
  public:

    MySimpleClass() {};

    MySimpleClass(int) {};

    ~MySimpleClass() {};  
};


int main() {
  MySimpleClass c;
  return 0;
}

In this example, the estimated local variable sizes are:

  • Constructor MySimpleClass::MySimpleClass(): Four bytes.

    The size comes from the this pointer, which is an implicit argument to the constructor. You specify the pointer size using the option Target processor type (-target).

  • Constructor MySimpleClass::MySimpleClass(int): Eight bytes.

    The size comes from the this pointer and the int argument.

  • Destructor MySimpleClass::~MySimpleClass(): Four bytes.

    The size comes from the this pointer.

  • main(): Five bytes.

    The size comes from the int return value and the size of object c. The minimum size of an object is the alignment that you specify using the option Target processor type (-target).

class MyClass {
  public:
    MyClass() {};
    MyClass(int) {};
    ~MyClass() {};
  private:
    int i[10];   
};
void func1(const MyClass& c) {
}


void func2() {
  func1(4);  
}

In this example, the estimated local variable size for func2() is 0. When func2() calls func1(), a temporary object of the class MyClass is created. The temporary variable is excluded from the calculation. Because there are no explicitly declared variables in the body of func2, the reported metric value is 0.

class WrapperA {
private:
	int       start;
	int       end;
public:

	virtual int get_start() {
		return start;
	}
};


WrapperA aG;

WrapperA get_WrapperA_1() {
	//...
	return aG;
}


WrapperA  get_WrapperA_2() {
	//...
	WrapperA bL;
	return bL;
}


int main(){
	return 1;
}

In this example, the functions get_WrapperA_1 and get_WrapperA_2 return objects of the class WrapperA. You might expect the estimate for get_WrapperA_1 to be equal to the size of aG. After return type optimization, get_wrapperA_1() contains a WrapperA* type pointer which points to a copy of aG. The lower estimate of the local variable size for get_wrapperA_1() is four bytes.

Similarly, after the return type optimization, get_WrapperA_2 contains a WrapperA object and a WrapperA* pointer. The lower estimate of the local variable size for get_wrapperA_2() is 16 bytes.

Metric Information

Group: Function
Acronym: LOCAL_VARS_MIN
HIS Metric: No

Version History

Introduced in R2016b