github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/compiler/sizes.go (about)

     1  package compiler
     2  
     3  import (
     4  	"go/types"
     5  )
     6  
     7  // The code in this file has been copied from
     8  // https://golang.org/src/go/types/sizes.go and modified to allow for int and
     9  // pointer sizes to differ.
    10  // The original license can be found here:
    11  //     https://golang.org/LICENSE
    12  
    13  type stdSizes struct {
    14  	IntSize  int64
    15  	PtrSize  int64
    16  	MaxAlign int64
    17  }
    18  
    19  func (s *stdSizes) Alignof(T types.Type) int64 {
    20  	// For arrays and structs, alignment is defined in terms
    21  	// of alignment of the elements and fields, respectively.
    22  	switch t := T.Underlying().(type) {
    23  	case *types.Array:
    24  		// spec: "For a variable x of array type: unsafe.Alignof(x)
    25  		// is the same as unsafe.Alignof(x[0]), but at least 1."
    26  		return s.Alignof(t.Elem())
    27  	case *types.Struct:
    28  		// spec: "For a variable x of struct type: unsafe.Alignof(x)
    29  		// is the largest of the values unsafe.Alignof(x.f) for each
    30  		// field f of x, but at least 1."
    31  		max := int64(1)
    32  		for i := 0; i < t.NumFields(); i++ {
    33  			f := t.Field(i)
    34  			if a := s.Alignof(f.Type()); a > max {
    35  				max = a
    36  			}
    37  		}
    38  		return max
    39  	case *types.Slice, *types.Interface:
    40  		// Multiword data structures are effectively structs
    41  		// in which each element has size WordSize.
    42  		return s.PtrSize
    43  	case *types.Basic:
    44  		// Strings are like slices and interfaces.
    45  		if t.Info()&types.IsString != 0 {
    46  			return s.PtrSize
    47  		}
    48  	case *types.Signature:
    49  		// Even though functions in tinygo are 2 pointers, they are not 2 pointer aligned
    50  		return s.PtrSize
    51  	}
    52  
    53  	a := s.Sizeof(T) // may be 0
    54  	// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
    55  	if a < 1 {
    56  		return 1
    57  	}
    58  	// complex{64,128} are aligned like [2]float{32,64}.
    59  	if t, ok := T.Underlying().(*types.Basic); ok && t.Info()&types.IsComplex != 0 {
    60  		a /= 2
    61  	}
    62  	if a > s.MaxAlign {
    63  		return s.MaxAlign
    64  	}
    65  	return a
    66  }
    67  
    68  func (s *stdSizes) Offsetsof(fields []*types.Var) []int64 {
    69  	offsets := make([]int64, len(fields))
    70  	var o int64
    71  	for i, f := range fields {
    72  		a := s.Alignof(f.Type())
    73  		o = align(o, a)
    74  		offsets[i] = o
    75  		o += s.Sizeof(f.Type())
    76  	}
    77  	return offsets
    78  }
    79  
    80  var basicSizes = [...]byte{
    81  	types.Bool:       1,
    82  	types.Int8:       1,
    83  	types.Int16:      2,
    84  	types.Int32:      4,
    85  	types.Int64:      8,
    86  	types.Uint8:      1,
    87  	types.Uint16:     2,
    88  	types.Uint32:     4,
    89  	types.Uint64:     8,
    90  	types.Float32:    4,
    91  	types.Float64:    8,
    92  	types.Complex64:  8,
    93  	types.Complex128: 16,
    94  }
    95  
    96  func (s *stdSizes) Sizeof(T types.Type) int64 {
    97  	switch t := T.Underlying().(type) {
    98  	case *types.Basic:
    99  		k := t.Kind()
   100  		if int(k) < len(basicSizes) {
   101  			if s := basicSizes[k]; s > 0 {
   102  				return int64(s)
   103  			}
   104  		}
   105  		if k == types.String {
   106  			return s.PtrSize * 2
   107  		}
   108  		if k == types.Int || k == types.Uint {
   109  			return s.IntSize
   110  		}
   111  		if k == types.Uintptr {
   112  			return s.PtrSize
   113  		}
   114  		if k == types.UnsafePointer {
   115  			return s.PtrSize
   116  		}
   117  		if k == types.Invalid {
   118  			return 0 // only relevant when there is a type error somewhere
   119  		}
   120  		panic("unknown basic type: " + t.String())
   121  	case *types.Array:
   122  		n := t.Len()
   123  		if n <= 0 {
   124  			return 0
   125  		}
   126  		// n > 0
   127  		a := s.Alignof(t.Elem())
   128  		z := s.Sizeof(t.Elem())
   129  		return align(z, a)*(n-1) + z
   130  	case *types.Slice:
   131  		return s.PtrSize * 3
   132  	case *types.Struct:
   133  		n := t.NumFields()
   134  		if n == 0 {
   135  			return 0
   136  		}
   137  		fields := make([]*types.Var, t.NumFields())
   138  		maxAlign := int64(1)
   139  		for i := range fields {
   140  			field := t.Field(i)
   141  			fields[i] = field
   142  			al := s.Alignof(field.Type())
   143  			if al > maxAlign {
   144  				maxAlign = al
   145  			}
   146  		}
   147  		// Pick the size that fits this struct and add some alignment. Some
   148  		// structs have some extra padding at the end which should also be taken
   149  		// care of:
   150  		//     struct { int32 n; byte b }
   151  		offsets := s.Offsetsof(fields)
   152  		return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign)
   153  	case *types.Interface:
   154  		return s.PtrSize * 2
   155  	case *types.Pointer, *types.Chan, *types.Map:
   156  		return s.PtrSize
   157  	case *types.Signature:
   158  		// Func values in TinyGo are two words in size.
   159  		return s.PtrSize * 2
   160  	default:
   161  		panic("unknown type: " + t.String())
   162  	}
   163  }
   164  
   165  // align returns the smallest y >= x such that y % a == 0.
   166  func align(x, a int64) int64 {
   167  	y := x + a - 1
   168  	return y - y%a
   169  }